Merge branch '3.4' into 4.2
* 3.4: fix translating file validation error message [Validator] Add missing Hungarian translations [3.4] [Validator] Add missing french validation translations. [Validator] Only traverse arrays that are cascaded into Handle case where no translations were found [Validator] Translate unique collection message to Hungarian fix tests Run test in separate process Use a class name that does not actually exist fix horizontal spacing of inlined Bootstrap forms [Translator] Warm up the translations cache in dev turn failed file uploads into form errors
This commit is contained in:
commit
ecb52bc9f1
@ -112,11 +112,11 @@
|
|||||||
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
|
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<div class="form-group{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
|
<div class="form-group{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
|
||||||
{{- form_label(form) -}}
|
{{- form_label(form) }} {# -#}
|
||||||
{{- form_widget(form, widget_attr) -}}
|
{{ form_widget(form, widget_attr) }} {# -#}
|
||||||
{{- form_help(form) -}}
|
{{ form_widget(form) }} {# -#}
|
||||||
{{- form_errors(form) -}}
|
{{ form_errors(form) }} {# -#}
|
||||||
</div>
|
</div> {# -#}
|
||||||
{%- endblock form_row %}
|
{%- endblock form_row %}
|
||||||
|
|
||||||
{% block button_row -%}
|
{% block button_row -%}
|
||||||
|
@ -70,6 +70,9 @@
|
|||||||
<tag name="form.type" />
|
<tag name="form.type" />
|
||||||
<argument type="service" id="form.choice_list_factory"/>
|
<argument type="service" id="form.choice_list_factory"/>
|
||||||
</service>
|
</service>
|
||||||
|
<service id="form.type.file" class="Symfony\Component\Form\Extension\Core\Type\FileType" public="true">
|
||||||
|
<argument type="service" id="translator" on-invalid="ignore" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="form.type_extension.form.transformation_failure_handling" class="Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension">
|
<service id="form.type_extension.form.transformation_failure_handling" class="Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension">
|
||||||
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
|
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
|
||||||
|
@ -778,6 +778,9 @@ class ClientTest extends TestCase
|
|||||||
$this->assertEquals([], $client->getCookieJar()->all(), '->restart() clears the cookies');
|
$this->assertEquals([], $client->getCookieJar()->all(), '->restart() clears the cookies');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @runInSeparateProcess
|
||||||
|
*/
|
||||||
public function testInsulatedRequests()
|
public function testInsulatedRequests()
|
||||||
{
|
{
|
||||||
$client = new TestClient();
|
$client = new TestClient();
|
||||||
|
@ -481,7 +481,7 @@ class ErrorHandlerTest extends TestCase
|
|||||||
|
|
||||||
public function testHandleErrorException()
|
public function testHandleErrorException()
|
||||||
{
|
{
|
||||||
$exception = new \Error("Class 'Foo' not found");
|
$exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found");
|
||||||
|
|
||||||
$handler = new ErrorHandler();
|
$handler = new ErrorHandler();
|
||||||
$handler->setExceptionHandler(function () use (&$args) {
|
$handler->setExceptionHandler(function () use (&$args) {
|
||||||
@ -491,7 +491,7 @@ class ErrorHandlerTest extends TestCase
|
|||||||
$handler->handleException($exception);
|
$handler->handleException($exception);
|
||||||
|
|
||||||
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
|
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
|
||||||
$this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
|
$this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,9 +25,9 @@ use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
|
|||||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
|
||||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
|
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
|
||||||
use Symfony\Component\DependencyInjection\TypedReference;
|
use Symfony\Component\DependencyInjection\TypedReference;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
|
||||||
|
|
||||||
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
||||||
|
|
||||||
@ -547,13 +547,17 @@ class AutowirePassTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||||
|
* @exceptedExceptionMessage Invalid service "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy": method "setLogger()" does not exist.
|
||||||
|
*/
|
||||||
public function testWithNonExistingSetterAndAutowiring()
|
public function testWithNonExistingSetterAndAutowiring()
|
||||||
{
|
{
|
||||||
$container = new ContainerBuilder();
|
$container = new ContainerBuilder();
|
||||||
|
|
||||||
$definition = $container->register(HttpKernelInterface::class, HttpKernelInterface::class)->setAutowired(true);
|
$definition = $container->register(CaseSensitiveClass::class, CaseSensitiveClass::class)->setAutowired(true);
|
||||||
$definition->addMethodCall('setLogger');
|
$definition->addMethodCall('setLogger');
|
||||||
$this->expectException(RuntimeException::class);
|
|
||||||
(new ResolveClassPass())->process($container);
|
(new ResolveClassPass())->process($container);
|
||||||
(new AutowireRequiredMethodsPass())->process($container);
|
(new AutowireRequiredMethodsPass())->process($container);
|
||||||
(new AutowirePass())->process($container);
|
(new AutowirePass())->process($container);
|
||||||
|
@ -17,13 +17,11 @@ use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
|
|||||||
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
|
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
|
||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
|
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
|
||||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
|
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
|
||||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists;
|
use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists;
|
||||||
use Symfony\Component\DependencyInjection\TypedReference;
|
use Symfony\Component\DependencyInjection\TypedReference;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
|
||||||
|
|
||||||
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
||||||
|
|
||||||
@ -115,6 +113,10 @@ class ResolveBindingsPassTest extends TestCase
|
|||||||
$this->assertEquals([['setDefaultLocale', ['fr']]], $definition->getMethodCalls());
|
$this->assertEquals([['setDefaultLocale', ['fr']]], $definition->getMethodCalls());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||||
|
* @exceptedExceptionMessage Invalid service "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy": method "setLogger()" does not exist.
|
||||||
|
*/
|
||||||
public function testWithNonExistingSetterAndBinding()
|
public function testWithNonExistingSetterAndBinding()
|
||||||
{
|
{
|
||||||
$container = new ContainerBuilder();
|
$container = new ContainerBuilder();
|
||||||
@ -123,36 +125,11 @@ class ResolveBindingsPassTest extends TestCase
|
|||||||
'$c' => (new Definition('logger'))->setFactory('logger'),
|
'$c' => (new Definition('logger'))->setFactory('logger'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$definition = $container->register(HttpKernelInterface::class, HttpKernelInterface::class);
|
$definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class);
|
||||||
$definition->addMethodCall('setLogger');
|
$definition->addMethodCall('setLogger');
|
||||||
$definition->setBindings($bindings);
|
$definition->setBindings($bindings);
|
||||||
$this->expectException(RuntimeException::class);
|
|
||||||
|
|
||||||
$pass = new ResolveBindingsPass();
|
$pass = new ResolveBindingsPass();
|
||||||
$pass->process($container);
|
$pass->process($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTupleBinding()
|
|
||||||
{
|
|
||||||
$container = new ContainerBuilder();
|
|
||||||
|
|
||||||
$bindings = [
|
|
||||||
'$c' => new BoundArgument(new Reference('bar')),
|
|
||||||
CaseSensitiveClass::class.'$c' => new BoundArgument(new Reference('foo')),
|
|
||||||
];
|
|
||||||
|
|
||||||
$definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class);
|
|
||||||
$definition->addMethodCall('setSensitiveClass');
|
|
||||||
$definition->addMethodCall('setAnotherC');
|
|
||||||
$definition->setBindings($bindings);
|
|
||||||
|
|
||||||
$pass = new ResolveBindingsPass();
|
|
||||||
$pass->process($container);
|
|
||||||
|
|
||||||
$expected = [
|
|
||||||
['setSensitiveClass', [new Reference('foo')]],
|
|
||||||
['setAnotherC', [new Reference('bar')]],
|
|
||||||
];
|
|
||||||
$this->assertEquals($expected, $definition->getMethodCalls());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,33 @@ namespace Symfony\Component\Form\Extension\Core\Type;
|
|||||||
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\FormError;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Form\FormView;
|
use Symfony\Component\Form\FormView;
|
||||||
use Symfony\Component\OptionsResolver\Options;
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class FileType extends AbstractType
|
class FileType extends AbstractType
|
||||||
{
|
{
|
||||||
|
const KIB_BYTES = 1024;
|
||||||
|
const MIB_BYTES = 1048576;
|
||||||
|
|
||||||
|
private static $suffixes = [
|
||||||
|
1 => 'bytes',
|
||||||
|
self::KIB_BYTES => 'KiB',
|
||||||
|
self::MIB_BYTES => 'MiB',
|
||||||
|
];
|
||||||
|
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
public function __construct(TranslatorInterface $translator = null)
|
||||||
|
{
|
||||||
|
$this->translator = $translator;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -43,6 +61,10 @@ class FileType extends AbstractType
|
|||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if ($requestHandler->isFileUpload($file)) {
|
if ($requestHandler->isFileUpload($file)) {
|
||||||
$data[] = $file;
|
$data[] = $file;
|
||||||
|
|
||||||
|
if (method_exists($requestHandler, 'getUploadFileError') && null !== $errorCode = $requestHandler->getUploadFileError($file)) {
|
||||||
|
$form->addError($this->getFileUploadError($errorCode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +76,8 @@ class FileType extends AbstractType
|
|||||||
}
|
}
|
||||||
|
|
||||||
$event->setData($data);
|
$event->setData($data);
|
||||||
|
} elseif ($requestHandler->isFileUpload($event->getData()) && method_exists($requestHandler, 'getUploadFileError') && null !== $errorCode = $requestHandler->getUploadFileError($event->getData())) {
|
||||||
|
$form->addError($this->getFileUploadError($errorCode));
|
||||||
} elseif (!$requestHandler->isFileUpload($event->getData())) {
|
} elseif (!$requestHandler->isFileUpload($event->getData())) {
|
||||||
$event->setData(null);
|
$event->setData(null);
|
||||||
}
|
}
|
||||||
@ -116,4 +140,109 @@ class FileType extends AbstractType
|
|||||||
{
|
{
|
||||||
return 'file';
|
return 'file';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getFileUploadError($errorCode)
|
||||||
|
{
|
||||||
|
$messageParameters = [];
|
||||||
|
|
||||||
|
if (UPLOAD_ERR_INI_SIZE === $errorCode) {
|
||||||
|
list($limitAsString, $suffix) = $this->factorizeSizes(0, self::getMaxFilesize());
|
||||||
|
$messageTemplate = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.';
|
||||||
|
$messageParameters = [
|
||||||
|
'{{ limit }}' => $limitAsString,
|
||||||
|
'{{ suffix }}' => $suffix,
|
||||||
|
];
|
||||||
|
} elseif (UPLOAD_ERR_FORM_SIZE === $errorCode) {
|
||||||
|
$messageTemplate = 'The file is too large.';
|
||||||
|
} else {
|
||||||
|
$messageTemplate = 'The file could not be uploaded.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->translator) {
|
||||||
|
$message = $this->translator->trans($messageTemplate, $messageParameters);
|
||||||
|
} else {
|
||||||
|
$message = strtr($messageTemplate, $messageParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FormError($message, $messageTemplate, $messageParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum size of an uploaded file as configured in php.ini.
|
||||||
|
*
|
||||||
|
* This method should be kept in sync with Symfony\Component\HttpFoundation\File\UploadedFile::getMaxFilesize().
|
||||||
|
*
|
||||||
|
* @return int The maximum size of an uploaded file in bytes
|
||||||
|
*/
|
||||||
|
private static function getMaxFilesize()
|
||||||
|
{
|
||||||
|
$iniMax = strtolower(ini_get('upload_max_filesize'));
|
||||||
|
|
||||||
|
if ('' === $iniMax) {
|
||||||
|
return PHP_INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
$max = ltrim($iniMax, '+');
|
||||||
|
if (0 === strpos($max, '0x')) {
|
||||||
|
$max = \intval($max, 16);
|
||||||
|
} elseif (0 === strpos($max, '0')) {
|
||||||
|
$max = \intval($max, 8);
|
||||||
|
} else {
|
||||||
|
$max = (int) $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (substr($iniMax, -1)) {
|
||||||
|
case 't': $max *= 1024;
|
||||||
|
// no break
|
||||||
|
case 'g': $max *= 1024;
|
||||||
|
// no break
|
||||||
|
case 'm': $max *= 1024;
|
||||||
|
// no break
|
||||||
|
case 'k': $max *= 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the limit to the smallest possible number
|
||||||
|
* (i.e. try "MB", then "kB", then "bytes").
|
||||||
|
*
|
||||||
|
* This method should be kept in sync with Symfony\Component\Validator\Constraints\FileValidator::factorizeSizes().
|
||||||
|
*/
|
||||||
|
private function factorizeSizes($size, $limit)
|
||||||
|
{
|
||||||
|
$coef = self::MIB_BYTES;
|
||||||
|
$coefFactor = self::KIB_BYTES;
|
||||||
|
|
||||||
|
$limitAsString = (string) ($limit / $coef);
|
||||||
|
|
||||||
|
// Restrict the limit to 2 decimals (without rounding! we
|
||||||
|
// need the precise value)
|
||||||
|
while (self::moreDecimalsThan($limitAsString, 2)) {
|
||||||
|
$coef /= $coefFactor;
|
||||||
|
$limitAsString = (string) ($limit / $coef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert size to the same measure, but round to 2 decimals
|
||||||
|
$sizeAsString = (string) round($size / $coef, 2);
|
||||||
|
|
||||||
|
// If the size and limit produce the same string output
|
||||||
|
// (due to rounding), reduce the coefficient
|
||||||
|
while ($sizeAsString === $limitAsString) {
|
||||||
|
$coef /= $coefFactor;
|
||||||
|
$limitAsString = (string) ($limit / $coef);
|
||||||
|
$sizeAsString = (string) round($size / $coef, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$limitAsString, self::$suffixes[$coef]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should be kept in sync with Symfony\Component\Validator\Constraints\FileValidator::moreDecimalsThan().
|
||||||
|
*/
|
||||||
|
private static function moreDecimalsThan($double, $numberOfDecimals)
|
||||||
|
{
|
||||||
|
return \strlen((string) $double) > \strlen(round($double, $numberOfDecimals));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Form\FormInterface;
|
|||||||
use Symfony\Component\Form\RequestHandlerInterface;
|
use Symfony\Component\Form\RequestHandlerInterface;
|
||||||
use Symfony\Component\Form\Util\ServerParams;
|
use Symfony\Component\Form\Util\ServerParams;
|
||||||
use Symfony\Component\HttpFoundation\File\File;
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,4 +116,16 @@ class HttpFoundationRequestHandler implements RequestHandlerInterface
|
|||||||
{
|
{
|
||||||
return $data instanceof File;
|
return $data instanceof File;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getUploadFileError($data)
|
||||||
|
{
|
||||||
|
if (!$data instanceof UploadedFile || $data->isValid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data->getError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,30 @@ class NativeRequestHandler implements RequestHandlerInterface
|
|||||||
return \is_array($data) && isset($data['error']) && \is_int($data['error']);
|
return \is_array($data) && isset($data['error']) && \is_int($data['error']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getUploadFileError($data)
|
||||||
|
{
|
||||||
|
if (!\is_array($data)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($data['error'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_int($data['error'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UPLOAD_ERR_OK === $data['error']) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data['error'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the method used to submit the request to the server.
|
* Returns the method used to submit the request to the server.
|
||||||
*
|
*
|
||||||
|
@ -360,6 +360,28 @@ abstract class AbstractRequestHandlerTest extends TestCase
|
|||||||
$this->assertFalse($this->requestHandler->isFileUpload($this->getInvalidFile()));
|
$this->assertFalse($this->requestHandler->isFileUpload($this->getInvalidFile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider uploadFileErrorCodes
|
||||||
|
*/
|
||||||
|
public function testFailedFileUploadIsTurnedIntoFormError($errorCode, $expectedErrorCode)
|
||||||
|
{
|
||||||
|
$this->assertSame($expectedErrorCode, $this->requestHandler->getUploadFileError($this->getFailedUploadedFile($errorCode)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadFileErrorCodes()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'no error' => [UPLOAD_ERR_OK, null],
|
||||||
|
'upload_max_filesize ini directive' => [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_INI_SIZE],
|
||||||
|
'MAX_FILE_SIZE from form' => [UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_FORM_SIZE],
|
||||||
|
'partially uploaded' => [UPLOAD_ERR_PARTIAL, UPLOAD_ERR_PARTIAL],
|
||||||
|
'no file upload' => [UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_FILE],
|
||||||
|
'missing temporary directory' => [UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_NO_TMP_DIR],
|
||||||
|
'write failure' => [UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_CANT_WRITE],
|
||||||
|
'stopped by extension' => [UPLOAD_ERR_EXTENSION, UPLOAD_ERR_EXTENSION],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function setRequestData($method, $data, $files = []);
|
abstract protected function setRequestData($method, $data, $files = []);
|
||||||
|
|
||||||
abstract protected function getRequestHandler();
|
abstract protected function getRequestHandler();
|
||||||
@ -368,6 +390,8 @@ abstract class AbstractRequestHandlerTest extends TestCase
|
|||||||
|
|
||||||
abstract protected function getInvalidFile();
|
abstract protected function getInvalidFile();
|
||||||
|
|
||||||
|
abstract protected function getFailedUploadedFile($errorCode);
|
||||||
|
|
||||||
protected function createForm($name, $method = null, $compound = false)
|
protected function createForm($name, $method = null, $compound = false)
|
||||||
{
|
{
|
||||||
$config = $this->createBuilder($name, $compound);
|
$config = $this->createBuilder($name, $compound);
|
||||||
|
@ -184,6 +184,128 @@ class FileTypeTest extends BaseTypeTest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider uploadFileErrorCodes
|
||||||
|
*/
|
||||||
|
public function testFailedFileUploadIsTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage)
|
||||||
|
{
|
||||||
|
$form = $this->factory
|
||||||
|
->createBuilder(static::TESTED_TYPE)
|
||||||
|
->setRequestHandler(new HttpFoundationRequestHandler())
|
||||||
|
->getForm();
|
||||||
|
$form->submit(new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'foo', null, null, $errorCode, true));
|
||||||
|
|
||||||
|
if (UPLOAD_ERR_OK === $errorCode) {
|
||||||
|
$this->assertTrue($form->isValid());
|
||||||
|
} else {
|
||||||
|
$this->assertFalse($form->isValid());
|
||||||
|
$this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider uploadFileErrorCodes
|
||||||
|
*/
|
||||||
|
public function testFailedFileUploadIsTurnedIntoFormErrorUsingNativeRequestHandler($errorCode, $expectedErrorMessage)
|
||||||
|
{
|
||||||
|
$form = $this->factory
|
||||||
|
->createBuilder(static::TESTED_TYPE)
|
||||||
|
->setRequestHandler(new NativeRequestHandler())
|
||||||
|
->getForm();
|
||||||
|
$form->submit([
|
||||||
|
'name' => 'foo.txt',
|
||||||
|
'type' => 'text/plain',
|
||||||
|
'tmp_name' => 'owfdskjasdfsa',
|
||||||
|
'error' => $errorCode,
|
||||||
|
'size' => 100,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (UPLOAD_ERR_OK === $errorCode) {
|
||||||
|
$this->assertTrue($form->isValid());
|
||||||
|
} else {
|
||||||
|
$this->assertFalse($form->isValid());
|
||||||
|
$this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider uploadFileErrorCodes
|
||||||
|
*/
|
||||||
|
public function testMultipleSubmittedFailedFileUploadsAreTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage)
|
||||||
|
{
|
||||||
|
$form = $this->factory
|
||||||
|
->createBuilder(static::TESTED_TYPE, null, [
|
||||||
|
'multiple' => true,
|
||||||
|
])
|
||||||
|
->setRequestHandler(new HttpFoundationRequestHandler())
|
||||||
|
->getForm();
|
||||||
|
$form->submit([
|
||||||
|
new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'foo', null, null, $errorCode, true),
|
||||||
|
new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'bar', null, null, $errorCode, true),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (UPLOAD_ERR_OK === $errorCode) {
|
||||||
|
$this->assertTrue($form->isValid());
|
||||||
|
} else {
|
||||||
|
$this->assertFalse($form->isValid());
|
||||||
|
$this->assertCount(2, $form->getErrors());
|
||||||
|
$this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage());
|
||||||
|
$this->assertSame($expectedErrorMessage, $form->getErrors()[1]->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider uploadFileErrorCodes
|
||||||
|
*/
|
||||||
|
public function testMultipleSubmittedFailedFileUploadsAreTurnedIntoFormErrorUsingNativeRequestHandler($errorCode, $expectedErrorMessage)
|
||||||
|
{
|
||||||
|
$form = $this->factory
|
||||||
|
->createBuilder(static::TESTED_TYPE, null, [
|
||||||
|
'multiple' => true,
|
||||||
|
])
|
||||||
|
->setRequestHandler(new NativeRequestHandler())
|
||||||
|
->getForm();
|
||||||
|
$form->submit([
|
||||||
|
[
|
||||||
|
'name' => 'foo.txt',
|
||||||
|
'type' => 'text/plain',
|
||||||
|
'tmp_name' => 'owfdskjasdfsa',
|
||||||
|
'error' => $errorCode,
|
||||||
|
'size' => 100,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'bar.txt',
|
||||||
|
'type' => 'text/plain',
|
||||||
|
'tmp_name' => 'owfdskjasdfsa',
|
||||||
|
'error' => $errorCode,
|
||||||
|
'size' => 100,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (UPLOAD_ERR_OK === $errorCode) {
|
||||||
|
$this->assertTrue($form->isValid());
|
||||||
|
} else {
|
||||||
|
$this->assertFalse($form->isValid());
|
||||||
|
$this->assertCount(2, $form->getErrors());
|
||||||
|
$this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage());
|
||||||
|
$this->assertSame($expectedErrorMessage, $form->getErrors()[1]->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadFileErrorCodes()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'no error' => [UPLOAD_ERR_OK, null],
|
||||||
|
'upload_max_filesize ini directive' => [UPLOAD_ERR_INI_SIZE, 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'],
|
||||||
|
'MAX_FILE_SIZE from form' => [UPLOAD_ERR_FORM_SIZE, 'The file is too large.'],
|
||||||
|
'partially uploaded' => [UPLOAD_ERR_PARTIAL, 'The file could not be uploaded.'],
|
||||||
|
'no file upload' => [UPLOAD_ERR_NO_FILE, 'The file could not be uploaded.'],
|
||||||
|
'missing temporary directory' => [UPLOAD_ERR_NO_TMP_DIR, 'The file could not be uploaded.'],
|
||||||
|
'write failure' => [UPLOAD_ERR_CANT_WRITE, 'The file could not be uploaded.'],
|
||||||
|
'stopped by extension' => [UPLOAD_ERR_EXTENSION, 'The file could not be uploaded.'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private function createUploadedFile(RequestHandlerInterface $requestHandler, $path, $originalName)
|
private function createUploadedFile(RequestHandlerInterface $requestHandler, $path, $originalName)
|
||||||
{
|
{
|
||||||
if ($requestHandler instanceof HttpFoundationRequestHandler) {
|
if ($requestHandler instanceof HttpFoundationRequestHandler) {
|
||||||
|
@ -56,4 +56,9 @@ class HttpFoundationRequestHandlerTest extends AbstractRequestHandlerTest
|
|||||||
{
|
{
|
||||||
return 'file:///etc/passwd';
|
return 'file:///etc/passwd';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getFailedUploadedFile($errorCode)
|
||||||
|
{
|
||||||
|
return new UploadedFile(__DIR__.'/../../Fixtures/foo', 'foo', null, null, $errorCode, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,4 +275,15 @@ class NativeRequestHandlerTest extends AbstractRequestHandlerTest
|
|||||||
'size' => '100',
|
'size' => '100',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getFailedUploadedFile($errorCode)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'upload.txt',
|
||||||
|
'type' => 'text/plain',
|
||||||
|
'tmp_name' => 'owfdskjasdfsa',
|
||||||
|
'error' => $errorCode,
|
||||||
|
'size' => 100,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Translation;
|
namespace Symfony\Component\Translation;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
|
||||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||||
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
|
||||||
use Symfony\Contracts\Translation\LocaleAwareInterface;
|
use Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||||
@ -19,7 +20,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||||||
/**
|
/**
|
||||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||||
*/
|
*/
|
||||||
class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface
|
class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface, WarmableInterface
|
||||||
{
|
{
|
||||||
const MESSAGE_DEFINED = 0;
|
const MESSAGE_DEFINED = 0;
|
||||||
const MESSAGE_MISSING = 1;
|
const MESSAGE_MISSING = 1;
|
||||||
@ -100,6 +101,16 @@ class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorIn
|
|||||||
return $this->translator->getCatalogue($locale);
|
return $this->translator->getCatalogue($locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function warmUp($cacheDir)
|
||||||
|
{
|
||||||
|
if ($this->translator instanceof WarmableInterface) {
|
||||||
|
$this->translator->warmUp($cacheDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the fallback locales.
|
* Gets the fallback locales.
|
||||||
*
|
*
|
||||||
|
@ -159,6 +159,11 @@ function printTitle($title)
|
|||||||
|
|
||||||
function printTable($translations, $verboseOutput)
|
function printTable($translations, $verboseOutput)
|
||||||
{
|
{
|
||||||
|
if (0 === count($translations)) {
|
||||||
|
echo 'No translations found';
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
$longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));
|
$longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));
|
||||||
|
|
||||||
foreach ($translations as $locale => $translation) {
|
foreach ($translations as $locale => $translation) {
|
||||||
|
@ -24,7 +24,9 @@
|
|||||||
"symfony/config": "~3.4|~4.0",
|
"symfony/config": "~3.4|~4.0",
|
||||||
"symfony/console": "~3.4|~4.0",
|
"symfony/console": "~3.4|~4.0",
|
||||||
"symfony/dependency-injection": "~3.4|~4.0",
|
"symfony/dependency-injection": "~3.4|~4.0",
|
||||||
|
"symfony/http-kernel": "~3.4|~4.0",
|
||||||
"symfony/intl": "~3.4|~4.0",
|
"symfony/intl": "~3.4|~4.0",
|
||||||
|
"symfony/var-dumper": "~3.4|~4.0",
|
||||||
"symfony/yaml": "~3.4|~4.0",
|
"symfony/yaml": "~3.4|~4.0",
|
||||||
"symfony/finder": "~2.8|~3.0|~4.0",
|
"symfony/finder": "~2.8|~3.0|~4.0",
|
||||||
"psr/log": "~1.0"
|
"psr/log": "~1.0"
|
||||||
|
@ -68,7 +68,7 @@ class Collection extends Composite
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$field instanceof Optional && !$field instanceof Required) {
|
if (!$field instanceof Optional && !$field instanceof Required) {
|
||||||
$this->fields[$fieldName] = $field = new Required($field);
|
$this->fields[$fieldName] = new Required($field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,6 +334,34 @@
|
|||||||
<source>This value should be valid JSON.</source>
|
<source>This value should be valid JSON.</source>
|
||||||
<target>Cette valeur doit être un JSON valide.</target>
|
<target>Cette valeur doit être un JSON valide.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="87">
|
||||||
|
<source>This collection should contain only unique elements.</source>
|
||||||
|
<target>Cette collection ne doit pas comporter de doublons.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="88">
|
||||||
|
<source>This value should be positive.</source>
|
||||||
|
<target>Cette valeur doit être strictement positive.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="89">
|
||||||
|
<source>This value should be either positive or zero.</source>
|
||||||
|
<target>Cette valeur doit être supérieure ou égale à zéro.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="90">
|
||||||
|
<source>This value should be negative.</source>
|
||||||
|
<target>Cette valeur doit être strictement négative.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="91">
|
||||||
|
<source>This value should be either negative or zero.</source>
|
||||||
|
<target>Cette valeur doit être inférieure ou égale à zéro.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="92">
|
||||||
|
<source>This value is not a valid timezone.</source>
|
||||||
|
<target>Cette valeur n'est pas un fuseau horaire valide.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="93">
|
||||||
|
<source>This password has been leaked in a data breach, it must not be used. Please use another password.</source>
|
||||||
|
<target>Ce mot de passe a été révélé par une faille de sécurité et ne doit plus être utilisé. Veuillez utiliser un autre mot de passe.</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -334,6 +334,30 @@
|
|||||||
<source>This value should be valid JSON.</source>
|
<source>This value should be valid JSON.</source>
|
||||||
<target>Ez az érték érvényes JSON kell, hogy legyen.</target>
|
<target>Ez az érték érvényes JSON kell, hogy legyen.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="87">
|
||||||
|
<source>This value should be positive.</source>
|
||||||
|
<target>Ennek az értéknek pozitívnak kell lennie.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="88">
|
||||||
|
<source>This value should be either positive or zero.</source>
|
||||||
|
<target>Ennek az értéknek pozitívnak vagy nullának kell lennie.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="89">
|
||||||
|
<source>This value should be negative.</source>
|
||||||
|
<target>Ennek az értéknek negatívnak kell lennie.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="90">
|
||||||
|
<source>This value should be either negative or zero.</source>
|
||||||
|
<target>Ennek az értéknek negatívnak vagy nullának kell lennie.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="91">
|
||||||
|
<source>This collection should contain only unique elements.</source>
|
||||||
|
<target>Ez a gyűjtemény csak egyedi elemeket tartalmazhat.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="92">
|
||||||
|
<source>This value is not a valid timezone.</source>
|
||||||
|
<target>Ez az érték nem egy érvényes időzóna.</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -589,6 +589,30 @@ abstract class AbstractValidatorTest extends TestCase
|
|||||||
$this->assertNull($violations[0]->getCode());
|
$this->assertNull($violations[0]->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testOnlyCascadedArraysAreTraversed()
|
||||||
|
{
|
||||||
|
$entity = new Entity();
|
||||||
|
$entity->reference = ['key' => new Reference()];
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) {
|
||||||
|
$context->addViolation('Message %param%', ['%param%' => 'value']);
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addPropertyConstraint('reference', new Callback([
|
||||||
|
'callback' => function () {},
|
||||||
|
'groups' => 'Group',
|
||||||
|
]));
|
||||||
|
$this->referenceMetadata->addConstraint(new Callback([
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
]));
|
||||||
|
|
||||||
|
$violations = $this->validate($entity, null, 'Group');
|
||||||
|
|
||||||
|
/* @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(0, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
public function testArrayTraversalCannotBeDisabled()
|
public function testArrayTraversalCannotBeDisabled()
|
||||||
{
|
{
|
||||||
$entity = new Entity();
|
$entity = new Entity();
|
||||||
|
@ -353,24 +353,18 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||||||
* Validates each object in a collection against the constraints defined
|
* Validates each object in a collection against the constraints defined
|
||||||
* for their classes.
|
* for their classes.
|
||||||
*
|
*
|
||||||
* If the parameter $recursive is set to true, nested {@link \Traversable}
|
* Nested arrays are also iterated.
|
||||||
* objects are iterated as well. Nested arrays are always iterated,
|
|
||||||
* regardless of the value of $recursive.
|
|
||||||
*
|
*
|
||||||
* @param iterable $collection The collection
|
* @param iterable $collection The collection
|
||||||
* @param string $propertyPath The current property path
|
* @param string $propertyPath The current property path
|
||||||
* @param (string|GroupSequence)[] $groups The validated groups
|
* @param (string|GroupSequence)[] $groups The validated groups
|
||||||
* @param ExecutionContextInterface $context The current execution context
|
* @param ExecutionContextInterface $context The current execution context
|
||||||
*
|
|
||||||
* @see ClassNode
|
|
||||||
* @see CollectionNode
|
|
||||||
*/
|
*/
|
||||||
private function validateEachObjectIn($collection, $propertyPath, array $groups, ExecutionContextInterface $context)
|
private function validateEachObjectIn($collection, $propertyPath, array $groups, ExecutionContextInterface $context)
|
||||||
{
|
{
|
||||||
foreach ($collection as $key => $value) {
|
foreach ($collection as $key => $value) {
|
||||||
if (\is_array($value)) {
|
if (\is_array($value)) {
|
||||||
// Arrays are always cascaded, independent of the specified
|
// Also traverse nested arrays
|
||||||
// traversal strategy
|
|
||||||
$this->validateEachObjectIn(
|
$this->validateEachObjectIn(
|
||||||
$value,
|
$value,
|
||||||
$propertyPath.'['.$key.']',
|
$propertyPath.'['.$key.']',
|
||||||
@ -600,7 +594,8 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||||||
* in the passed metadata object. Then, if the value is an instance of
|
* in the passed metadata object. Then, if the value is an instance of
|
||||||
* {@link \Traversable} and the selected traversal strategy permits it,
|
* {@link \Traversable} and the selected traversal strategy permits it,
|
||||||
* the value is traversed and each nested object validated against its own
|
* the value is traversed and each nested object validated against its own
|
||||||
* constraints. Arrays are always traversed.
|
* constraints. If the value is an array, it is traversed regardless of
|
||||||
|
* the given strategy.
|
||||||
*
|
*
|
||||||
* @param mixed $value The validated value
|
* @param mixed $value The validated value
|
||||||
* @param object|null $object The current object
|
* @param object|null $object The current object
|
||||||
@ -659,8 +654,8 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||||||
|
|
||||||
$cascadingStrategy = $metadata->getCascadingStrategy();
|
$cascadingStrategy = $metadata->getCascadingStrategy();
|
||||||
|
|
||||||
// Quit unless we have an array or a cascaded object
|
// Quit unless we cascade
|
||||||
if (!\is_array($value) && !($cascadingStrategy & CascadingStrategy::CASCADE)) {
|
if (!($cascadingStrategy & CascadingStrategy::CASCADE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user