Compare commits

..

7 commits

Author SHA1 Message Date
Jan Böhmer
9e8e404d70 New translations messages.en.xlf (German) 2025-09-04 16:11:14 +02:00
Jan Böhmer
91719d0c3c New translations messages.en.xlf (German) 2025-09-03 16:01:25 +02:00
Jan Böhmer
457f9afbd2 New translations messages.en.xlf (German) 2025-09-03 14:55:47 +02:00
Jan Böhmer
6b103b02d9 New translations messages.en.xlf (Czech) 2025-09-01 09:46:17 +02:00
Jan Böhmer
ffb8b69ec9 New translations messages.en.xlf (Czech) 2025-08-31 19:21:12 +02:00
Jan Böhmer
4329e6b871 New translations messages.en.xlf (Czech) 2025-08-31 18:21:50 +02:00
Jan Böhmer
ed109932b5 New translations messages.en.xlf (Czech) 2025-08-31 17:25:07 +02:00
31 changed files with 1127 additions and 1383 deletions

View file

@ -56,16 +56,12 @@ export default class MarkdownController extends Controller {
this.element.innerHTML = DOMPurify.sanitize(MarkdownController._marked.parse(this.unescapeHTML(raw)));
for(let a of this.element.querySelectorAll('a')) {
// test if link is absolute
var r = new RegExp('^(?:[a-z+]+:)?//', 'i');
if (r.test(a.getAttribute('href'))) {
//Mark all links as external
a.classList.add('link-external');
//Open links in new tag
a.setAttribute('target', '_blank');
//Dont track
a.setAttribute('rel', 'noopener');
}
//Mark all links as external
a.classList.add('link-external');
//Open links in new tag
a.setAttribute('target', '_blank');
//Dont track
a.setAttribute('rel', 'noopener');
}
//Apply bootstrap styles to tables
@ -112,4 +108,4 @@ export default class MarkdownController extends Controller {
gfm: true,
});
}*/
}
}

View file

@ -71,8 +71,6 @@
--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);
--ck-color-button-on-color: var(--bs-primary)
--ck-content-font-color: var(--ck-color-base-text);
}
}

View file

@ -25,7 +25,8 @@
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^3.2.0",
"dompdf/dompdf": "^v3.0.0",
"part-db/swap-bundle": "^6.0.0",
"florianv/swap": "^4.0",
"florianv/swap-bundle": "dev-master",
"gregwar/captcha-bundle": "^2.1.0",
"hshn/base64-encoded-file": "^5.0",
"jbtronics/2fa-webauthn": "^3.0.0",

651
composer.lock generated

File diff suppressed because it is too large Load diff

View file

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

View file

@ -359,10 +359,6 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
label: "perm.revert_elements"
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles', 'delete_profiles']
apiTokenRole: ROLE_API_EDIT
import:
label: "perm.import"
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles' ]
apiTokenRole: ROLE_API_EDIT
api:
label: "perm.api"

View file

@ -28,14 +28,9 @@ It is recommended to install Part-DB on a 64-bit system, as the 32-bit version o
For the installation of Part-DB, we need some prerequisites. They can be installed by running the following command:
```bash
sudo apt update && apt upgrade
sudo apt install git curl zip ca-certificates software-properties-common \
apt-transport-https lsb-release nano wget sqlite3
sudo apt install git curl zip ca-certificates software-properties-common apt-transport-https lsb-release nano wget
```
Please run `sqlite3 --version` to assert that the SQLite version is 3.35 or higher.
Otherwise some database migrations will not succeed.
### Install PHP and apache2
Part-DB is written in [PHP](https://php.net) and therefore needs a PHP interpreter to run. Part-DB needs PHP 8.2 or

View file

@ -1,64 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\DataFixtures;
use App\Entity\PriceInformations\Currency;
use Brick\Math\BigDecimal;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class CurrencyFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
$currency1 = new Currency();
$currency1->setName('US-Dollar');
$currency1->setIsoCode('USD');
$manager->persist($currency1);
$currency2 = new Currency();
$currency2->setName('Swiss Franc');
$currency2->setIsoCode('CHF');
$currency2->setExchangeRate(BigDecimal::of('0.91'));
$manager->persist($currency2);
$currency3 = new Currency();
$currency3->setName('Great British Pound');
$currency3->setIsoCode('GBP');
$currency3->setExchangeRate(BigDecimal::of('0.78'));
$manager->persist($currency3);
$currency7 = new Currency();
$currency7->setName('Test Currency with long name');
$currency7->setIsoCode('CNY');
$manager->persist($currency7);
$manager->flush();
//Ensure that currency 7 gets ID 7
$manager->getRepository(Currency::class)->changeID($currency7, 7);
$manager->flush();
}
}

View file

@ -41,7 +41,6 @@ use App\Entity\Attachments\UserAttachment;
use RuntimeException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use function in_array;
@ -57,7 +56,7 @@ final class AttachmentVoter extends Voter
{
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
//This voter only works for attachments
@ -66,8 +65,7 @@ final class AttachmentVoter extends Voter
}
if ($attribute === 'show_private') {
$vote?->addReason('User is not allowed to view private attachments.');
return $this->helper->isGranted($token, 'attachments', 'show_private', $vote);
return $this->helper->isGranted($token, 'attachments', 'show_private');
}
@ -113,8 +111,7 @@ final class AttachmentVoter extends Voter
throw new RuntimeException('Encountered unknown Parameter type: ' . $subject);
}
$vote?->addReason('User is not allowed to '.$this->mapOperation($attribute).' attachments of type '.$param.'.');
return $this->helper->isGranted($token, $param, $this->mapOperation($attribute), $vote);
return $this->helper->isGranted($token, $param, $this->mapOperation($attribute));
}
return false;

View file

@ -25,7 +25,6 @@ namespace App\Security\Voter;
use App\Entity\UserSystem\Group;
use App\Services\UserSystem\VoterHelper;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -44,9 +43,9 @@ final class GroupVoter extends Voter
*
* @param string $attribute
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
return $this->helper->isGranted($token, 'groups', $attribute, $vote);
return $this->helper->isGranted($token, 'groups', $attribute);
}
/**

View file

@ -26,7 +26,6 @@ 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\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
@ -48,16 +47,9 @@ final class ImpersonateUserVoter extends Voter
&& $subject instanceof UserInterface;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$result = $this->helper->isGranted($token, 'users', 'impersonate');
if ($result === false) {
$vote?->addReason('User is not allowed to impersonate other users.');
$this->helper->addReason($vote, 'users', 'impersonate');
}
return $result;
return $this->helper->isGranted($token, 'users', 'impersonate');
}
public function supportsAttribute(string $attribute): bool
@ -69,4 +61,4 @@ final class ImpersonateUserVoter extends Voter
{
return is_a($subjectType, User::class, true);
}
}
}

View file

@ -44,7 +44,6 @@ namespace App\Security\Voter;
use App\Entity\LabelSystem\LabelProfile;
use App\Services\UserSystem\VoterHelper;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -59,15 +58,14 @@ final class LabelProfileVoter extends Voter
'delete' => 'delete_profiles',
'show_history' => 'show_history',
'revert_element' => 'revert_element',
'import' => 'import',
];
public function __construct(private readonly VoterHelper $helper)
{}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
return $this->helper->isGranted($token, 'labels', self::MAPPING[$attribute], $vote);
return $this->helper->isGranted($token, 'labels', self::MAPPING[$attribute]);
}
protected function supports($attribute, $subject): bool

View file

@ -26,7 +26,6 @@ use App\Services\UserSystem\VoterHelper;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\LogSystem\AbstractLogEntry;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -40,7 +39,7 @@ final class LogEntryVoter extends Voter
{
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $this->helper->resolveUser($token);
@ -49,19 +48,19 @@ final class LogEntryVoter extends Voter
}
if ('delete' === $attribute) {
return $this->helper->isGranted($token, 'system', 'delete_logs', $vote);
return $this->helper->isGranted($token, 'system', 'delete_logs');
}
if ('read' === $attribute) {
//Allow read of the users own log entries
if (
$subject->getUser() === $user
&& $this->helper->isGranted($token, 'self', 'show_logs', $vote)
&& $this->helper->isGranted($token, 'self', 'show_logs')
) {
return true;
}
return $this->helper->isGranted($token, 'system', 'show_logs', $vote);
return $this->helper->isGranted($token, 'system', 'show_logs');
}
if ('show_details' === $attribute) {

View file

@ -46,7 +46,6 @@ use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Parts\Part;
use App\Entity\PriceInformations\Orderdetail;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -60,7 +59,7 @@ final class OrderdetailVoter extends Voter
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): 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!');
@ -76,7 +75,7 @@ final class OrderdetailVoter extends Voter
//If we have no part associated use the generic part permission
if (is_string($subject) || !$subject->getPart() instanceof Part) {
return $this->helper->isGranted($token, 'parts', $operation, $vote);
return $this->helper->isGranted($token, 'parts', $operation);
}
//Otherwise vote on the part

View file

@ -39,7 +39,6 @@ use App\Entity\Parameters\StorageLocationParameter;
use App\Entity\Parameters\SupplierParameter;
use RuntimeException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -54,7 +53,7 @@ final class ParameterVoter extends Voter
{
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
@ -109,7 +108,7 @@ final class ParameterVoter extends Voter
throw new RuntimeException('Encountered unknown Parameter type: ' . (is_object($subject) ? $subject::class : $subject));
}
return $this->helper->isGranted($token, $param, $attribute, $vote);
return $this->helper->isGranted($token, $param, $attribute);
}
protected function supports(string $attribute, $subject): bool

View file

@ -46,7 +46,6 @@ 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\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -62,7 +61,7 @@ final class PartAssociationVoter extends Voter
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
if (!is_string($subject) && !$subject instanceof PartAssociation) {
throw new \RuntimeException('Invalid subject type!');
@ -78,7 +77,7 @@ final class PartAssociationVoter extends Voter
//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, $vote);
return $this->helper->isGranted($token, 'parts', $operation);
}
//Otherwise vote on the part

View file

@ -46,7 +46,6 @@ use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -60,13 +59,13 @@ final class PartLotVoter extends Voter
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element', 'withdraw', 'add', 'move'];
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $this->helper->resolveUser($token);
if (in_array($attribute, ['withdraw', 'add', 'move'], true))
{
$base_permission = $this->helper->isGranted($token, 'parts_stock', $attribute, $vote);
$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.
@ -74,10 +73,6 @@ final class PartLotVoter extends Voter
$lot_permission = $subject->getOwner() === $user || $subject->getOwner()->getID() === $user->getID();
}
if (!$lot_permission) {
$vote->addReason('User is not the owner of the lot.');
}
return $base_permission && $lot_permission;
}
@ -91,7 +86,7 @@ final class PartLotVoter extends Voter
//If we have no part associated use the generic part permission
if (is_string($subject) || !$subject->getPart() instanceof Part) {
return $this->helper->isGranted($token, 'parts', $operation, $vote);
return $this->helper->isGranted($token, 'parts', $operation);
}
//Otherwise vote on the part

View file

@ -25,7 +25,6 @@ namespace App\Security\Voter;
use App\Entity\Parts\Part;
use App\Services\UserSystem\VoterHelper;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -53,9 +52,10 @@ final class PartVoter extends Voter
return false;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
return $this->helper->isGranted($token, 'parts', $attribute, $vote);
//Null concealing operator means, that no
return $this->helper->isGranted($token, 'parts', $attribute);
}
public function supportsAttribute(string $attribute): bool

View file

@ -24,7 +24,6 @@ namespace App\Security\Voter;
use App\Services\UserSystem\VoterHelper;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -40,17 +39,12 @@ final class PermissionVoter extends Voter
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$attribute = ltrim($attribute, '@');
[$perm, $op] = explode('.', $attribute);
$result = $this->helper->isGranted($token, $perm, $op);
if ($result === false) {
$this->helper->addReason($vote, $perm, $op);
}
return $result;
return $this->helper->isGranted($token, $perm, $op);
}
public function supportsAttribute(string $attribute): bool

View file

@ -47,7 +47,6 @@ use App\Entity\PriceInformations\Orderdetail;
use App\Entity\Parts\Part;
use App\Entity\PriceInformations\Pricedetail;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
@ -61,7 +60,7 @@ final class PricedetailVoter extends Voter
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$operation = match ($attribute) {
'read' => 'read',
@ -73,7 +72,7 @@ final class PricedetailVoter extends Voter
//If we have no part associated use the generic part permission
if (is_string($subject) || !$subject->getOrderdetail() instanceof Orderdetail || !$subject->getOrderdetail()->getPart() instanceof Part) {
return $this->helper->isGranted($token, 'parts', $operation, $vote);
return $this->helper->isGranted($token, 'parts', $operation);
}
//Otherwise vote on the part

View file

@ -33,7 +33,6 @@ use App\Entity\Parts\Supplier;
use App\Entity\PriceInformations\Currency;
use App\Services\UserSystem\VoterHelper;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use function is_object;
@ -114,10 +113,10 @@ final class StructureVoter extends Voter
*
* @param string $attribute
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$permission_name = $this->instanceToPermissionName($subject);
//Just resolve the permission
return $this->helper->isGranted($token, $permission_name, $attribute, $vote);
return $this->helper->isGranted($token, $permission_name, $attribute);
}
}

View file

@ -26,7 +26,6 @@ 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\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use function in_array;
@ -80,7 +79,7 @@ final class UserVoter extends Voter
*
* @param string $attribute
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $this->helper->resolveUser($token);
@ -98,7 +97,7 @@ final class UserVoter extends Voter
if (($subject instanceof User) && $subject->getID() === $user->getID() &&
$this->helper->isValidOperation('self', $attribute)) {
//Then we also need to check the self permission
$tmp = $this->helper->isGranted($token, 'self', $attribute, $vote);
$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;
@ -107,7 +106,7 @@ final class UserVoter extends Voter
//Else just check user permission:
if ($this->helper->isValidOperation('users', $attribute)) {
return $this->helper->isGranted($token, 'users', $attribute, $vote);
return $this->helper->isGranted($token, 'users', $attribute);
}
return false;

View file

@ -123,11 +123,11 @@ class LCSCProvider implements InfoProviderInterface
*/
private function queryByTerm(string $term): array
{
$response = $this->lcscClient->request('POST', self::ENDPOINT_URL . "/search/v2/global", [
$response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/search/global", [
'headers' => [
'Cookie' => new Cookie('currencyCode', $this->settings->currency)
],
'json' => [
'query' => [
'keyword' => $term,
],
]);
@ -165,9 +165,6 @@ class LCSCProvider implements InfoProviderInterface
if ($field === null) {
return null;
}
// Replace "range" indicators with mathematical tilde symbols
// so they don't get rendered as strikethrough by Markdown
$field = preg_replace("/~/", "\u{223c}", $field);
return strip_tags($field);
}
@ -200,6 +197,9 @@ class LCSCProvider implements InfoProviderInterface
$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(

View file

@ -26,8 +26,6 @@ use App\Entity\PriceInformations\Currency;
use App\Settings\SystemSettings\LocalizationSettings;
use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;
use Exchanger\Exception\UnsupportedCurrencyPairException;
use Exchanger\Exception\UnsupportedExchangeQueryException;
use Swap\Swap;
class ExchangeRateUpdater
@ -41,21 +39,15 @@ class ExchangeRateUpdater
*/
public function update(Currency $currency): Currency
{
try {
//Try it in the direction QUOTE/BASE first, as most providers provide rates in this direction
$rate = $this->swap->latest($currency->getIsoCode().'/'.$this->localizationSettings->baseCurrency);
$effective_rate = BigDecimal::of($rate->getValue());
} catch (UnsupportedCurrencyPairException|UnsupportedExchangeQueryException $exception) {
//Otherwise try to get it inverse and calculate it ourselfes, from the format "BASE/QUOTE"
$rate = $this->swap->latest($this->localizationSettings->baseCurrency.'/'.$currency->getIsoCode());
//The rate says how many quote units are worth one base unit
//So we need to invert it to get the exchange rate
//Currency pairs are always in the format "BASE/QUOTE"
$rate = $this->swap->latest($this->localizationSettings->baseCurrency.'/'.$currency->getIsoCode());
//The rate says how many quote units are worth one base unit
//So we need to invert it to get the exchange rate
$rate_bd = BigDecimal::of($rate->getValue());
$effective_rate = BigDecimal::one()->dividedBy($rate_bd, Currency::PRICE_SCALE, RoundingMode::HALF_UP);
}
$rate_bd = BigDecimal::of($rate->getValue());
$rate_inverted = BigDecimal::one()->dividedBy($rate_bd, Currency::PRICE_SCALE, RoundingMode::HALF_UP);
$currency->setExchangeRate($effective_rate);
$currency->setExchangeRate($rate_inverted);
return $currency;
}

View file

@ -28,9 +28,6 @@ use App\Repository\UserRepository;
use App\Security\ApiTokenAuthenticatedToken;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @see \App\Tests\Services\UserSystem\VoterHelperTest
@ -38,14 +35,10 @@ use Symfony\Contracts\Translation\TranslatorInterface;
final class VoterHelper
{
private readonly UserRepository $userRepository;
private readonly array $permissionStructure;
public function __construct(private readonly PermissionManager $permissionManager,
private readonly TranslatorInterface $translator,
private readonly EntityManagerInterface $entityManager)
public function __construct(private readonly PermissionManager $permissionManager, private readonly EntityManagerInterface $entityManager)
{
$this->userRepository = $this->entityManager->getRepository(User::class);
$this->permissionStructure = $this->permissionManager->getPermissionStructure();
}
/**
@ -54,16 +47,11 @@ final class VoterHelper
* @param TokenInterface $token The token to check
* @param string $permission The permission to check
* @param string $operation The operation to check
* @param Vote|null $vote The vote object to add reasons to (optional). If null, no reasons are added.
* @return bool
*/
public function isGranted(TokenInterface $token, string $permission, string $operation, ?Vote $vote = null): bool
public function isGranted(TokenInterface $token, string $permission, string $operation): bool
{
$tmp = $this->isGrantedTrinary($token, $permission, $operation) ?? false;
if ($tmp === false) {
$this->addReason($vote, $permission, $operation);
}
return $tmp;
return $this->isGrantedTrinary($token, $permission, $operation) ?? false;
}
/**
@ -136,17 +124,4 @@ final class VoterHelper
{
return $this->permissionManager->isValidOperation($permission, $operation);
}
public function addReason(?Vote $voter, string $permission, $operation): void
{
if ($voter !== null) {
$voter->addReason(sprintf("User does not have permission %s -> %s -> %s (%s.%s).",
$this->translator->trans('perm.group.'.($this->permissionStructure['perms'][$permission]['group'] ?? 'unknown') ),
$this->translator->trans($this->permissionStructure['perms'][$permission]['label'] ?? $permission),
$this->translator->trans($this->permissionStructure['perms'][$permission]['operations'][$operation]['label'] ?? $operation),
$permission,
$operation
));
}
}
}
}

View file

@ -133,6 +133,15 @@
"ekino/phpstan-banned-code": {
"version": "v0.3.1"
},
"florianv/exchanger": {
"version": "1.4.1"
},
"florianv/swap": {
"version": "3.5.0"
},
"florianv/swap-bundle": {
"version": "5.0.x-dev"
},
"gregwar/captcha": {
"version": "v1.1.7"
},
@ -245,9 +254,6 @@
"./config/packages/datatables.yaml"
]
},
"part-db/swap-bundle": {
"version": "v6.0.0"
},
"php-http/discovery": {
"version": "1.18",
"recipe": {

View file

@ -1,9 +1,6 @@
{% extends "bundles/TwigBundle/Exception/error.html.twig" %}
{% block status_comment %}
Nice try! But you are not allowed to do this!<br>
<code>{{ exception.message }}</code>
Nice try! But you are not allowed to do this!
<br> <small>If you think you should have access to this ressource, contact the adminstrator.</small>
{% endblock %}
{% endblock %}

View file

@ -23,7 +23,7 @@
</div>
<div class="col-6">
{% if provider.providerInfo.settings_class is defined %}
<a href="{{ path('info_providers_provider_settings', {'provider': provider.providerKey}) }}" class="btn btn-primary btn-sm {% if not is_granted('@config.change_system_settings') %}disabled{% endif %}"
<a href="{{ path('info_providers_provider_settings', {'provider': provider.providerKey}) }}" class="btn btn-primary btn-sm"
title="{% trans %}info_providers.settings.title{% endtrans %}"
><i class="fa-solid fa-cog"></i></a>
{% endif %}

View file

@ -1,35 +0,0 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Tests\Controller\AdminPages;
use App\Entity\PriceInformations\Currency;
use PHPUnit\Framework\Attributes\Group;
use App\Entity\Parts\Manufacturer;
#[Group('slow')]
#[Group('DB')]
class CurrencyController extends AbstractAdminController
{
protected static string $base_path = '/en/currency';
protected static string $entity_class = Currency::class;
}

View file

@ -7164,8 +7164,8 @@ Exampletown</target>
Element 2
Element 3
Element 1 -> Element 1.1
Element 1 -> Element 1.2]]></target>
Element 1 -&gt; Element 1.1
Element 1 -&gt; Element 1.2]]></target>
</segment>
</unit>
<unit id="TWSqPFi" name="entity.mass_creation.btn">

1518
yarn.lock

File diff suppressed because it is too large Load diff