mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Add form and types to handle async files
This commit is contained in:
parent
1195767eb3
commit
28d09a8206
@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\Entity;
|
namespace Chill\DocStoreBundle\Entity;
|
||||||
|
|
||||||
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
||||||
use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists;
|
use Chill\DocStoreBundle\Validator\Constraints\AsyncFileExists;
|
||||||
use ChampsLibres\WopiLib\Contract\Entity\Document;
|
use ChampsLibres\WopiLib\Contract\Entity\Document;
|
||||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
|
@ -14,13 +14,10 @@ namespace Chill\DocStoreBundle\Form;
|
|||||||
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
|
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
|
||||||
use Chill\DocStoreBundle\Entity\Document;
|
use Chill\DocStoreBundle\Entity\Document;
|
||||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Doctrine\Persistence\ObjectManager;
|
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
@ -29,33 +26,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||||||
|
|
||||||
class AccompanyingCourseDocumentType extends AbstractType
|
class AccompanyingCourseDocumentType extends AbstractType
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ObjectManager
|
|
||||||
*/
|
|
||||||
protected $om;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the user running this form.
|
|
||||||
*
|
|
||||||
* @var User
|
|
||||||
*/
|
|
||||||
protected $user;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||||
) {
|
) {}
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
|
@ -20,23 +20,15 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||||||
|
|
||||||
class DocumentCategoryType extends AbstractType
|
class DocumentCategoryType extends AbstractType
|
||||||
{
|
{
|
||||||
private $chillBundlesFlipped;
|
|
||||||
|
|
||||||
public function __construct($kernelBundles)
|
|
||||||
{
|
|
||||||
// TODO faire un service dans CHillMain
|
|
||||||
foreach ($kernelBundles as $key => $value) {
|
|
||||||
if (str_starts_with((string) $key, 'Chill')) {
|
|
||||||
$this->chillBundlesFlipped[$value] = $key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
|
$bundles = [
|
||||||
|
'chill-doc-store' => 'chill-doc-store',
|
||||||
|
];
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
->add('bundleId', ChoiceType::class, [
|
->add('bundleId', ChoiceType::class, [
|
||||||
'choices' => $this->chillBundlesFlipped,
|
'choices' => $bundles,
|
||||||
'disabled' => false,
|
'disabled' => false,
|
||||||
])
|
])
|
||||||
->add('idInsideBundle', null, [
|
->add('idInsideBundle', null, [
|
||||||
@ -44,7 +36,7 @@ class DocumentCategoryType extends AbstractType
|
|||||||
])
|
])
|
||||||
->add('documentClass', null, [
|
->add('documentClass', null, [
|
||||||
'disabled' => false,
|
'disabled' => false,
|
||||||
]) // cahcerh par default PersonDocument
|
])
|
||||||
->add('name', TranslatableStringFormType::class);
|
->add('name', TranslatableStringFormType::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\DocStoreBundle\Form;
|
namespace Chill\DocStoreBundle\Form;
|
||||||
|
|
||||||
use ChampsLibres\AsyncUploaderBundle\Form\Type\AsyncUploaderType;
|
use Chill\DocStoreBundle\Form\Type\AsyncUploaderType;
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
@ -26,15 +26,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||||||
*/
|
*/
|
||||||
class StoredObjectType extends AbstractType
|
class StoredObjectType extends AbstractType
|
||||||
{
|
{
|
||||||
/**
|
public function __construct(private readonly EntityManagerInterface $em) {}
|
||||||
* @var EntityManagerInterface
|
|
||||||
*/
|
|
||||||
protected $em;
|
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $em)
|
|
||||||
{
|
|
||||||
$this->em = $em;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Form\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormView;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
|
||||||
|
class AsyncUploaderType extends AbstractType
|
||||||
|
{
|
||||||
|
private readonly int $expires_delay;
|
||||||
|
private readonly int $max_submit_delay;
|
||||||
|
private readonly int $max_post_file_size;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly UrlGeneratorInterface $url_generator,
|
||||||
|
ParameterBagInterface $parameters,
|
||||||
|
) {
|
||||||
|
$config = $parameters->get('chill_doc_store')['openstack']['temp_url'];
|
||||||
|
|
||||||
|
$this->expires_delay = $config['max_expires_delay'];
|
||||||
|
$this->max_submit_delay = $config['max_submit_delay'];
|
||||||
|
$this->max_post_file_size = $config['max_post_file_size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'expires_delay' => $this->expires_delay,
|
||||||
|
'max_post_size' => $this->max_post_file_size,
|
||||||
|
'submit_delay' => $this->max_submit_delay,
|
||||||
|
'max_files' => 1,
|
||||||
|
'error_bubbling' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('expires_delay', ['int']);
|
||||||
|
$resolver->setAllowedTypes('max_post_size', ['int']);
|
||||||
|
$resolver->setAllowedTypes('max_files', ['int']);
|
||||||
|
$resolver->setAllowedTypes('submit_delay', ['int']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildView(
|
||||||
|
FormView $view,
|
||||||
|
FormInterface $form,
|
||||||
|
array $options
|
||||||
|
) {
|
||||||
|
$view->vars['attr']['data-async-file-upload'] = true;
|
||||||
|
$view->vars['attr']['data-generate-temp-url-post'] = $this
|
||||||
|
->url_generator->generate('async_upload.generate_url', [
|
||||||
|
'expires_delay' => $options['expires_delay'],
|
||||||
|
'method' => 'post',
|
||||||
|
'submit_delay' => $options['submit_delay'],
|
||||||
|
]);
|
||||||
|
$view->vars['attr']['data-temp-url-get'] = $this->url_generator
|
||||||
|
->generate('async_upload.generate_url', ['method' => 'GET']);
|
||||||
|
$view->vars['attr']['data-max-files'] = $options['max_files'];
|
||||||
|
$view->vars['attr']['data-max-post-size'] = $options['max_post_size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent()
|
||||||
|
{
|
||||||
|
return HiddenType::class;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Tests\Validator\Constraints;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\AsyncUpload\SignedUrl;
|
||||||
|
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Validator\Constraints\AsyncFileExists;
|
||||||
|
use Chill\DocStoreBundle\Validator\Constraints\AsyncFileExistsValidator;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Component\HttpClient\MockHttpClient;
|
||||||
|
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||||
|
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class AsyncFileExistsValidatorTest extends ConstraintValidatorTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
protected function createValidator()
|
||||||
|
{
|
||||||
|
$client = new MockHttpClient(function ($method, $url, $options): MockResponse {
|
||||||
|
if (str_contains((string) $url, '404')) {
|
||||||
|
return new MockResponse('', ['http_code' => 404]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MockResponse('', ['http_code' => 200]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$generator = $this->prophesize(TempUrlGeneratorInterface::class);
|
||||||
|
$generator->generate(Argument::in(['GET', 'HEAD']), Argument::type('string'), Argument::any())
|
||||||
|
->will(fn (array $args): SignedUrl => new SignedUrl($args[0], 'https://object.store.example/container/'.$args[1], new \DateTimeImmutable('1 hours')));
|
||||||
|
|
||||||
|
return new AsyncFileExistsValidator($generator->reveal(), $client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWhenFileExistsIsValid(): void
|
||||||
|
{
|
||||||
|
$this->validator->validate((new StoredObject())->setFilename('present'), new AsyncFileExists());
|
||||||
|
|
||||||
|
$this->assertNoViolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWhenFileIsNotPresent(): void
|
||||||
|
{
|
||||||
|
$this->validator->validate(
|
||||||
|
(new StoredObject())->setFilename('is_404'),
|
||||||
|
new AsyncFileExists(['message' => 'my_message'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->buildViolation('my_message')->setParameter('{{ filename }}', 'is_404')->assertRaised();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
*/
|
||||||
|
final class AsyncFileExists extends Constraint
|
||||||
|
{
|
||||||
|
public string $message = "The file '{{ filename }}' is not stored properly.";
|
||||||
|
|
||||||
|
public function validatedBy()
|
||||||
|
{
|
||||||
|
return AsyncFileExistsValidator::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTargets()
|
||||||
|
{
|
||||||
|
return [Constraint::CLASS_CONSTRAINT, Constraint::PROPERTY_CONSTRAINT];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Validator\Constraints;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
|
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
final class AsyncFileExistsValidator extends ConstraintValidator
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly TempUrlGeneratorInterface $tempUrlGenerator,
|
||||||
|
private readonly HttpClientInterface $client
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function validate($value, Constraint $constraint): void
|
||||||
|
{
|
||||||
|
if ($value instanceof StoredObject) {
|
||||||
|
$this->validateObject($value->getFilename(), $constraint);
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
$this->validateObject($value, $constraint);
|
||||||
|
} else {
|
||||||
|
throw new UnexpectedValueException($value, StoredObject::class.' or string');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateObject(string $file, Constraint $constraint): void
|
||||||
|
{
|
||||||
|
if (!$constraint instanceof AsyncFileExists) {
|
||||||
|
throw new UnexpectedTypeException($constraint, AsyncFileExists::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$urlHead = $this->tempUrlGenerator->generate(
|
||||||
|
'HEAD',
|
||||||
|
$file,
|
||||||
|
30
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->client->request('HEAD', $urlHead->url);
|
||||||
|
|
||||||
|
if (404 === $response->getStatusCode()) {
|
||||||
|
$this->context->buildViolation($constraint->message)
|
||||||
|
->setParameter('{{ filename }}', $file)
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
} catch (HttpExceptionInterface $exception) {
|
||||||
|
if (404 !== $exception->getResponse()->getStatusCode()) {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,18 +8,6 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- { name: doctrine.repository_service }
|
- { name: doctrine.repository_service }
|
||||||
|
|
||||||
Chill\DocStoreBundle\Form\DocumentCategoryType:
|
|
||||||
class: Chill\DocStoreBundle\Form\DocumentCategoryType
|
|
||||||
arguments: [ "%kernel.bundles%" ]
|
|
||||||
tags:
|
|
||||||
- { name: form.type }
|
|
||||||
|
|
||||||
Chill\DocStoreBundle\Form\PersonDocumentType:
|
|
||||||
class: Chill\DocStoreBundle\Form\PersonDocumentType
|
|
||||||
# arguments:
|
|
||||||
# - "@chill.main.helper.translatable_string"
|
|
||||||
tags:
|
|
||||||
- { name: form.type, alias: chill_docstorebundle_form_document }
|
|
||||||
|
|
||||||
Chill\DocStoreBundle\Security\Authorization\:
|
Chill\DocStoreBundle\Security\Authorization\:
|
||||||
resource: "./../Security/Authorization"
|
resource: "./../Security/Authorization"
|
||||||
@ -54,6 +42,9 @@ services:
|
|||||||
Chill\DocStoreBundle\GenericDoc\Renderer\:
|
Chill\DocStoreBundle\GenericDoc\Renderer\:
|
||||||
resource: '../GenericDoc/Renderer/'
|
resource: '../GenericDoc/Renderer/'
|
||||||
|
|
||||||
|
Chill\DocStoreBundle\Validator\:
|
||||||
|
resource: '../Validator'
|
||||||
|
|
||||||
Chill\DocStoreBundle\AsyncUpload\Driver\:
|
Chill\DocStoreBundle\AsyncUpload\Driver\:
|
||||||
resource: '../AsyncUpload/Driver/'
|
resource: '../AsyncUpload/Driver/'
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
services:
|
services:
|
||||||
Chill\DocStoreBundle\Form\StoredObjectType:
|
_defaults:
|
||||||
arguments:
|
autowire: true
|
||||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
autoconfigure: true
|
||||||
tags:
|
|
||||||
- { name: form.type }
|
|
||||||
|
|
||||||
Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType:
|
Chill\DocStoreBundle\Form\:
|
||||||
class: Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType
|
resource: '../../Form'
|
||||||
arguments:
|
|
||||||
- "@chill.main.helper.translatable_string"
|
Chill\DocStoreBundle\Form\PersonDocumentType:
|
||||||
tags:
|
tags:
|
||||||
- { name: form.type, alias: chill_docstorebundle_form_document }
|
- { name: form.type, alias: chill_docstorebundle_form_document }
|
||||||
|
|
||||||
|
Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType:
|
||||||
|
tags:
|
||||||
|
- { name: form.type, alias: chill_docstorebundle_form_document }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user