mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 09:35:49 +02:00
Show an error message in table instead of a 500 error when MySQL encounters an invalid Regex expression
This fixes issue #289
This commit is contained in:
parent
bafbd63610
commit
c50a80e8df
5 changed files with 146 additions and 29 deletions
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\DataTables\ErrorDataTable;
|
||||||
use App\DataTables\Filters\PartFilter;
|
use App\DataTables\Filters\PartFilter;
|
||||||
use App\DataTables\Filters\PartSearchFilter;
|
use App\DataTables\Filters\PartSearchFilter;
|
||||||
use App\DataTables\PartsDataTable;
|
use App\DataTables\PartsDataTable;
|
||||||
|
@ -33,6 +34,7 @@ use App\Entity\Parts\Supplier;
|
||||||
use App\Form\Filters\PartFilterType;
|
use App\Form\Filters\PartFilterType;
|
||||||
use App\Services\Parts\PartsTableActionHandler;
|
use App\Services\Parts\PartsTableActionHandler;
|
||||||
use App\Services\Trees\NodesListBuilder;
|
use App\Services\Trees\NodesListBuilder;
|
||||||
|
use Doctrine\DBAL\Exception\DriverException;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Omines\DataTablesBundle\DataTableFactory;
|
use Omines\DataTablesBundle\DataTableFactory;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
@ -41,6 +43,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class PartListsController extends AbstractController
|
class PartListsController extends AbstractController
|
||||||
{
|
{
|
||||||
|
@ -48,11 +51,14 @@ class PartListsController extends AbstractController
|
||||||
private NodesListBuilder $nodesListBuilder;
|
private NodesListBuilder $nodesListBuilder;
|
||||||
private DataTableFactory $dataTableFactory;
|
private DataTableFactory $dataTableFactory;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager, NodesListBuilder $nodesListBuilder, DataTableFactory $dataTableFactory)
|
private TranslatorInterface $translator;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager, NodesListBuilder $nodesListBuilder, DataTableFactory $dataTableFactory, TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
$this->entityManager = $entityManager;
|
$this->entityManager = $entityManager;
|
||||||
$this->nodesListBuilder = $nodesListBuilder;
|
$this->nodesListBuilder = $nodesListBuilder;
|
||||||
$this->dataTableFactory = $dataTableFactory;
|
$this->dataTableFactory = $dataTableFactory;
|
||||||
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,7 +150,21 @@ class PartListsController extends AbstractController
|
||||||
->handleRequest($request);
|
->handleRequest($request);
|
||||||
|
|
||||||
if ($table->isCallback()) {
|
if ($table->isCallback()) {
|
||||||
|
try {
|
||||||
return $table->getResponse();
|
return $table->getResponse();
|
||||||
|
} catch (DriverException $driverException) {
|
||||||
|
if ($driverException->getCode() === 1139) {
|
||||||
|
|
||||||
|
//Show only the part after "1139"
|
||||||
|
$regex_message = preg_replace('/^.*1139 /', '', $driverException->getMessage());
|
||||||
|
|
||||||
|
$errors = $this->translator->trans('part.table.invalid_regex') . ': ' . $regex_message;
|
||||||
|
|
||||||
|
return ErrorDataTable::errorTable($this->dataTableFactory, $request, $errors);
|
||||||
|
} else {
|
||||||
|
throw $driverException;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render($template, array_merge([
|
return $this->render($template, array_merge([
|
||||||
|
|
85
src/DataTables/ErrorDataTable.php
Normal file
85
src/DataTables/ErrorDataTable.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\DataTables;
|
||||||
|
|
||||||
|
use App\DataTables\Column\RowClassColumn;
|
||||||
|
use App\Entity\Parts\Part;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
$optionsResolver = new OptionsResolver();
|
||||||
|
$this->configureOptions($optionsResolver);
|
||||||
|
$options = $optionsResolver->resolve($options);
|
||||||
|
|
||||||
|
$dataTable
|
||||||
|
->add('dont_matter_we_only_set_color', RowClassColumn::class, [
|
||||||
|
'render' => function ($value, $context) {
|
||||||
|
return 'table-warning';
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('error', TextColumn::class, [
|
||||||
|
'label' => 'error_table.error',
|
||||||
|
'render' => function ($value, $context) {
|
||||||
|
return '<i class="fa-solid fa-triangle-exclamation fa-fw"></i> ' . $value;
|
||||||
|
},
|
||||||
|
])
|
||||||
|
;
|
||||||
|
|
||||||
|
//Build the array containing data
|
||||||
|
$data = [];
|
||||||
|
foreach ($options['errors'] as $error) {
|
||||||
|
$data[] = ['error' => $error];
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataTable->createAdapter(ArrayAdapter::class, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function errorTable(DataTableFactory $dataTableFactory, Request $request, $errors): Response
|
||||||
|
{
|
||||||
|
$error_table = $dataTableFactory->createFromType(self::class, ['errors' => $errors]);
|
||||||
|
$error_table->handleRequest($request);
|
||||||
|
return $error_table->getResponse();
|
||||||
|
}
|
||||||
|
}
|
|
@ -10960,31 +10960,31 @@ Element 3</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="Qt585vm" name="attachment.max_file_size">
|
<unit id="Qt585vm" name="attachment.max_file_size">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>attachment.max_file_size</source>
|
<source>attachment.max_file_size</source>
|
||||||
<target>Maximum file size</target>
|
<target>Maximum file size</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="tkkbiag" name="user.saml_user">
|
<unit id="tkkbiag" name="user.saml_user">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>user.saml_user</source>
|
<source>user.saml_user</source>
|
||||||
<target>SSO / SAML user</target>
|
<target>SSO / SAML user</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="fhepjKr" name="user.saml_user.pw_change_hint">
|
<unit id="fhepjKr" name="user.saml_user.pw_change_hint">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>user.saml_user.pw_change_hint</source>
|
<source>user.saml_user.pw_change_hint</source>
|
||||||
<target>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!</target>
|
<target>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!</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="32beTBH" name="login.sso_saml_login">
|
<unit id="32beTBH" name="login.sso_saml_login">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>login.sso_saml_login</source>
|
<source>login.sso_saml_login</source>
|
||||||
<target>Single Sign-On Login (SSO)</target>
|
<target>Single Sign-On Login (SSO)</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="wnMLanX" name="login.local_login_hint">
|
<unit id="wnMLanX" name="login.local_login_hint">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>login.local_login_hint</source>
|
<source>login.local_login_hint</source>
|
||||||
<target>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.</target>
|
<target>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.</target>
|
||||||
</segment>
|
</segment>
|
||||||
|
@ -11188,106 +11188,118 @@ Element 3</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="qDVJg2i" name="measurement_unit.new">
|
<unit id="qDVJg2i" name="measurement_unit.new">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>measurement_unit.new</source>
|
<source>measurement_unit.new</source>
|
||||||
<target>New Measurement Unit</target>
|
<target>New Measurement Unit</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="WKzr7h0" name="measurement_unit.edit">
|
<unit id="WKzr7h0" name="measurement_unit.edit">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>measurement_unit.edit</source>
|
<source>measurement_unit.edit</source>
|
||||||
<target>Edit Measurement Unit</target>
|
<target>Edit Measurement Unit</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="gRatnCn" name="user.aboutMe.label">
|
<unit id="gRatnCn" name="user.aboutMe.label">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>user.aboutMe.label</source>
|
<source>user.aboutMe.label</source>
|
||||||
<target>About Me</target>
|
<target>About Me</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="WsHXARp" name="storelocation.owner.label">
|
<unit id="WsHXARp" name="storelocation.owner.label">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>storelocation.owner.label</source>
|
<source>storelocation.owner.label</source>
|
||||||
<target>Owner</target>
|
<target>Owner</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="VQ97Dh0" name="storelocation.part_owner_must_match.label">
|
<unit id="VQ97Dh0" name="storelocation.part_owner_must_match.label">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>storelocation.part_owner_must_match.label</source>
|
<source>storelocation.part_owner_must_match.label</source>
|
||||||
<target>Part Lot owner must match storage location owner</target>
|
<target>Part Lot owner must match storage location owner</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="sddE1L." name="part_lot.owner">
|
<unit id="sddE1L." name="part_lot.owner">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>part_lot.owner</source>
|
<source>part_lot.owner</source>
|
||||||
<target>Owner</target>
|
<target>Owner</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="XKIMr8j" name="part_lot.owner.help">
|
<unit id="XKIMr8j" name="part_lot.owner.help">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>part_lot.owner.help</source>
|
<source>part_lot.owner.help</source>
|
||||||
<target>Only the owner can withdraw or add stock to this lot.</target>
|
<target>Only the owner can withdraw or add stock to this lot.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="61.yfNy" name="log.element_edited.changed_fields.owner">
|
<unit id="61.yfNy" name="log.element_edited.changed_fields.owner">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>log.element_edited.changed_fields.owner</source>
|
<source>log.element_edited.changed_fields.owner</source>
|
||||||
<target>Owner</target>
|
<target>Owner</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="YkZAIS8" name="log.element_edited.changed_fields.instock_unknown">
|
<unit id="YkZAIS8" name="log.element_edited.changed_fields.instock_unknown">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>log.element_edited.changed_fields.instock_unknown</source>
|
<source>log.element_edited.changed_fields.instock_unknown</source>
|
||||||
<target>Amount unknown</target>
|
<target>Amount unknown</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="xf7NNZ9" name="log.element_edited.changed_fields.needs_refill">
|
<unit id="xf7NNZ9" name="log.element_edited.changed_fields.needs_refill">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>log.element_edited.changed_fields.needs_refill</source>
|
<source>log.element_edited.changed_fields.needs_refill</source>
|
||||||
<target>Refill needed</target>
|
<target>Refill needed</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="Gfw_MWL" name="part.withdraw.access_denied">
|
<unit id="Gfw_MWL" name="part.withdraw.access_denied">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>part.withdraw.access_denied</source>
|
<source>part.withdraw.access_denied</source>
|
||||||
<target>Not allowed to do the desired action. Please check your permissions and the owner of the part lots.</target>
|
<target>Not allowed to do the desired action. Please check your permissions and the owner of the part lots.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="Dwo4KWP" name="part.info.amount.less_than_desired">
|
<unit id="Dwo4KWP" name="part.info.amount.less_than_desired">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>part.info.amount.less_than_desired</source>
|
<source>part.info.amount.less_than_desired</source>
|
||||||
<target>Less than desired</target>
|
<target>Less than desired</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="cdnsW4q" name="log.cli_user">
|
<unit id="cdnsW4q" name="log.cli_user">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>log.cli_user</source>
|
<source>log.cli_user</source>
|
||||||
<target>CLI user</target>
|
<target>CLI user</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="4GTAJ9E" name="log.element_edited.changed_fields.part_owner_must_match">
|
<unit id="4GTAJ9E" name="log.element_edited.changed_fields.part_owner_must_match">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>log.element_edited.changed_fields.part_owner_must_match</source>
|
<source>log.element_edited.changed_fields.part_owner_must_match</source>
|
||||||
<target>Part owner must match storage location owner</target>
|
<target>Part owner must match storage location owner</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="u6qFa_j" name="part.filter.lessThanDesired">
|
<unit id="u6qFa_j" name="part.filter.lessThanDesired">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>part.filter.lessThanDesired</source>
|
<source>part.filter.lessThanDesired</source>
|
||||||
<target>In stock less than desired (total amount < min. amount)</target>
|
<target><![CDATA[In stock less than desired (total amount < min. amount)]]></target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="lHTN.a1" name="part.filter.lotOwner">
|
<unit id="lHTN.a1" name="part.filter.lotOwner">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>part.filter.lotOwner</source>
|
<source>part.filter.lotOwner</source>
|
||||||
<target>Lot owner</target>
|
<target>Lot owner</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="47OCK_W" name="user.show_email_on_profile.label">
|
<unit id="47OCK_W" name="user.show_email_on_profile.label">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>user.show_email_on_profile.label</source>
|
<source>user.show_email_on_profile.label</source>
|
||||||
<target>Show email on public profile page</target>
|
<target>Show email on public profile page</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="qk2u9Qg" name="error_table.error">
|
||||||
|
<segment>
|
||||||
|
<source>error_table.error</source>
|
||||||
|
<target>An error occured during your request.</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="tLXzED2" name="part.table.invalid_regex">
|
||||||
|
<segment>
|
||||||
|
<source>part.table.invalid_regex</source>
|
||||||
|
<target>Invalid Regex expression</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="Dpb9AmY" name="saml.error.cannot_login_local_user_per_saml">
|
<unit id="Dpb9AmY" name="saml.error.cannot_login_local_user_per_saml">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>saml.error.cannot_login_local_user_per_saml</source>
|
<source>saml.error.cannot_login_local_user_per_saml</source>
|
||||||
<target>You can not login as local user via SSO! Use your local user password instead.</target>
|
<target>You can not login as local user via SSO! Use your local user password instead.</target>
|
||||||
</segment>
|
</segment>
|
||||||
|
|
|
@ -300,19 +300,19 @@
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="m8kMFhf" name="validator.attachment.name_not_blank">
|
<unit id="m8kMFhf" name="validator.attachment.name_not_blank">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>validator.attachment.name_not_blank</source>
|
<source>validator.attachment.name_not_blank</source>
|
||||||
<target>Set a value here, or upload a file to automatically use its filename as name for the attachment.</target>
|
<target>Set a value here, or upload a file to automatically use its filename as name for the attachment.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="nwGaNBW" name="validator.part_lot.owner_must_match_storage_location_owner">
|
<unit id="nwGaNBW" name="validator.part_lot.owner_must_match_storage_location_owner">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>validator.part_lot.owner_must_match_storage_location_owner</source>
|
<source>validator.part_lot.owner_must_match_storage_location_owner</source>
|
||||||
<target>The owner of this lot must match the owner of the selected storage location (%owner_name%)!</target>
|
<target>The owner of this lot must match the owner of the selected storage location (%owner_name%)!</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="HXSz3nQ" name="validator.part_lot.owner_must_not_be_anonymous">
|
<unit id="HXSz3nQ" name="validator.part_lot.owner_must_not_be_anonymous">
|
||||||
<segment state="translated">
|
<segment>
|
||||||
<source>validator.part_lot.owner_must_not_be_anonymous</source>
|
<source>validator.part_lot.owner_must_not_be_anonymous</source>
|
||||||
<target>A lot owner must not be the anonymous user!</target>
|
<target>A lot owner must not be the anonymous user!</target>
|
||||||
</segment>
|
</segment>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue