diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php index 4e2970138..ac832b34b 100644 --- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php +++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php @@ -22,6 +22,7 @@ use Chill\DocStoreBundle\Repository\DocumentCategoryRepository; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -45,6 +46,8 @@ class ActivityContext implements private PersonRenderInterface $personRender; + private PersonRepository $personRepository; + private TranslatableStringHelperInterface $translatableStringHelper; private TranslatorInterface $translator; @@ -55,6 +58,7 @@ class ActivityContext implements TranslatableStringHelperInterface $translatableStringHelper, EntityManagerInterface $em, PersonRenderInterface $personRender, + PersonRepository $personRepository, TranslatorInterface $translator, BaseContextData $baseContextData ) { @@ -63,6 +67,7 @@ class ActivityContext implements $this->translatableStringHelper = $translatableStringHelper; $this->em = $em; $this->personRender = $personRender; + $this->personRepository = $personRepository; $this->translator = $translator; $this->baseContextData = $baseContextData; } @@ -206,6 +211,32 @@ class ActivityContext implements return $options['mainPerson'] || $options['person1'] || $options['person2']; } + public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array + { + $normalized = []; + + foreach (['mainPerson', 'person1', 'person2'] as $k) { + $normalized[$k] = null === $data[$k] ? null : $data[$k]->getId(); + } + + return $normalized; + } + + public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array + { + $denormalized = []; + + foreach (['mainPerson', 'person1', 'person2'] as $k) { + if (null !== ($id = ($data[$k] ?? null))) { + $denormalized[$k] = $this->personRepository->find($id); + } else { + $denormalized[$k] = null; + } + } + + return $denormalized; + } + /** * @param Activity $entity */ diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php index 3189307f9..7e1873710 100644 --- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php +++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php @@ -146,6 +146,16 @@ class ListActivitiesByAccompanyingPeriodContext implements return $this->accompanyingPeriodContext->hasPublicForm($template, $entity); } + public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array + { + return $this->accompanyingPeriodContext->contextGenerationDataNormalize($template, $entity, $data); + } + + public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array + { + return $this->accompanyingPeriodContext->contextGenerationDataDenormalize($template, $entity, $data); + } + public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void { $this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData); diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 461137454..4dd67ceb8 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -77,7 +77,7 @@ Choose a type: Choisir un type 4 hours: 4 heures 4 hours 30: 4 heures 30 5 hours: 5 heures -Concerned groups: Parties concernées +Concerned groups: Parties concernées par l'échange Persons in accompanying course: Usagers du parcours Third persons: Tiers non-pro. Others persons: Usagers diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/edit.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/edit.html.twig index df814282a..8978739ea 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/edit.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/edit.html.twig @@ -7,7 +7,7 @@
{# <=== vue component: mainUser #} -

{{ 'Concerned groups'|trans }}

+

{{ 'Concerned groups calendar'|trans }}

{%- if form.persons is defined -%} {{ form_widget(form.persons) }} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/new.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/new.html.twig index 84eca97a0..9fff55d63 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/new.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/new.html.twig @@ -7,7 +7,7 @@
{# <=== vue component: mainUser #} -

{{ 'Concerned groups'|trans }}

+

{{ 'Concerned groups calendar'|trans }}

{%- if form.mainUser is defined -%} {{ form_row(form.mainUser) }} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig index bff4baa53..d48844f67 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig @@ -14,7 +14,7 @@
{{ entity.mainUser }}
-

{{ 'Concerned groups'|trans }}

+

{{ 'Concerned groups calendar'|trans }}

{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': 'calendar_' ~ context, 'render': 'bloc' } %} diff --git a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php index 9ba9e36b4..4984c359a 100644 --- a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php +++ b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php @@ -226,6 +226,16 @@ final class CalendarContext implements CalendarContextInterface return true; } + public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array + { + // TODO: Implement publicFormTransform() method. + } + + public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array + { + // TODO: Implement publicFormReverseTransform() method. + } + /** * @param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData */ diff --git a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php index d02cdc2c2..eeef3b417 100644 --- a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php +++ b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContextInterface.php @@ -56,6 +56,10 @@ interface CalendarContextInterface extends DocGeneratorContextWithPublicFormInte */ public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool; + public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array; + + public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array; + /** * @param Calendar $entity */ diff --git a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml index 623eb02cb..aa933bb63 100644 --- a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml @@ -4,7 +4,7 @@ My calendar list: Mes rendez-vous There is no calendar items.: Il n'y a pas de rendez-vous Remove calendar item: Supprimer le rendez-vous Are you sure you want to remove the calendar item?: Êtes-vous sûr de vouloir supprimer le rendez-vous? -Concerned groups: Parties concernées +Concerned groups calendar: Parties concernées Calendar data: Données du rendez-vous Update calendar: Modifier le rendez-vous main user concerned: Utilisateur concerné diff --git a/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php b/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php index f013c8435..4f58bd049 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php +++ b/src/Bundle/ChillDocGeneratorBundle/Context/DocGeneratorContextWithPublicFormInterface.php @@ -23,6 +23,9 @@ interface DocGeneratorContextWithPublicFormInterface extends DocGeneratorContext */ public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void; + /** + * Fill the form with initial data + */ public function getFormData(DocGeneratorTemplate $template, $entity): array; /** @@ -31,4 +34,14 @@ interface DocGeneratorContextWithPublicFormInterface extends DocGeneratorContext * @param mixed $entity */ public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool; + + /** + * Transform the data from the form into serializable data, storable into messenger's message + */ + public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array; + + /** + * Reverse the data from the messenger's message into data usable for doc's generation + */ + public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array; } diff --git a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php index d618f758a..6f153acd8 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php +++ b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php @@ -16,67 +16,57 @@ use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface; use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException; use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface; -use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException; use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository; +use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface; +use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage; use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Serializer\Model\Collection; use Doctrine\ORM\EntityManagerInterface; -use Exception; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\FileType; -use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; // TODO à mettre dans services use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; -use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Contracts\HttpClient\HttpClientInterface; -use Throwable; use function strlen; final class DocGeneratorTemplateController extends AbstractController { - private HttpClientInterface $client; - private ContextManager $contextManager; private DocGeneratorTemplateRepository $docGeneratorTemplateRepository; - private DriverInterface $driver; - private EntityManagerInterface $entityManager; - private LoggerInterface $logger; + private GeneratorInterface $generator; + + private MessageBusInterface $messageBus; private PaginatorFactory $paginatorFactory; - private StoredObjectManagerInterface $storedObjectManager; - public function __construct( ContextManager $contextManager, DocGeneratorTemplateRepository $docGeneratorTemplateRepository, - DriverInterface $driver, - LoggerInterface $logger, + GeneratorInterface $generator, + MessageBusInterface $messageBus, PaginatorFactory $paginatorFactory, - HttpClientInterface $client, - StoredObjectManagerInterface $storedObjectManager, EntityManagerInterface $entityManager ) { $this->contextManager = $contextManager; $this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository; - $this->driver = $driver; - $this->logger = $logger; + $this->generator = $generator; + $this->messageBus = $messageBus; $this->paginatorFactory = $paginatorFactory; - $this->client = $client; - $this->storedObjectManager = $storedObjectManager; $this->entityManager = $entityManager; } @@ -94,7 +84,6 @@ final class DocGeneratorTemplateController extends AbstractController ): Response { return $this->generateDocFromTemplate( $template, - $entityClassName, $entityId, $request, true @@ -115,7 +104,6 @@ final class DocGeneratorTemplateController extends AbstractController ): Response { return $this->generateDocFromTemplate( $template, - $entityClassName, $entityId, $request, false @@ -185,7 +173,6 @@ final class DocGeneratorTemplateController extends AbstractController private function generateDocFromTemplate( DocGeneratorTemplate $template, - string $entityClassName, int $entityId, Request $request, bool $isTest @@ -206,7 +193,7 @@ final class DocGeneratorTemplateController extends AbstractController if (null === $entity) { throw new NotFoundHttpException( - sprintf('Entity with classname %s and id %s is not found', $entityClassName, $entityId) + sprintf('Entity with classname %s and id %s is not found', $context->getEntityClass(), $entityId) ); } @@ -259,99 +246,68 @@ final class DocGeneratorTemplateController extends AbstractController } } - $document = $template->getFile(); - - if ($isTest && ($contextGenerationData['test_file'] instanceof File)) { - $dataDecrypted = file_get_contents($contextGenerationData['test_file']->getPathname()); - } else { - try { - $dataDecrypted = $this->storedObjectManager->read($document); - } catch (Throwable $exception) { - throw $exception; - } - } + // transform context generation data + $contextGenerationDataSanitized = + $context instanceof DocGeneratorContextWithPublicFormInterface ? + $context->contextGenerationDataNormalize($template, $entity, $contextGenerationData) + : []; + // if is test, render the data or generate the doc if ($isTest && isset($form) && $form['show_data']->getData()) { return $this->render('@ChillDocGenerator/Generator/debug_value.html.twig', [ 'datas' => json_encode($context->getData($template, $entity, $contextGenerationData), JSON_PRETTY_PRINT) ]); - } - - try { - $generatedResource = $this - ->driver - ->generateFromString( - $dataDecrypted, - $template->getFile()->getType(), - $context->getData($template, $entity, $contextGenerationData), - $template->getFile()->getFilename() - ); - } catch (TemplateException $e) { - return new Response( - implode("\n", $e->getErrors()), - 400, - [ - 'Content-Type' => 'text/plain', - ] + } elseif ($isTest) { + $generated = $this->generator->generateDocFromTemplate( + $template, + $entityId, + $contextGenerationDataSanitized, + null, + true, + isset($form) ? $form['test_file']->getData() : null ); - } - if ($isTest) { return new Response( - $generatedResource, + $generated, Response::HTTP_OK, [ 'Content-Transfer-Encoding', 'binary', 'Content-Type' => 'application/vnd.oasis.opendocument.text', 'Content-Disposition' => 'attachment; filename="generated.odt"', - 'Content-Length' => strlen($generatedResource), + 'Content-Length' => strlen($generated), ], ); } - /** @var StoredObject $storedObject */ - $storedObject = (new ObjectNormalizer()) - ->denormalize( - [ - 'type' => $template->getFile()->getType(), - 'filename' => sprintf('%s_odt', uniqid('doc_', true)), - ], - StoredObject::class - ); - - try { - $this->storedObjectManager->write($storedObject, $generatedResource); - } catch (Throwable $exception) { - throw $exception; - } + // this is not a test + // we prepare the object to store the document + $storedObject = (new StoredObject()) + ->setStatus(StoredObject::STATUS_PENDING) + ; $this->entityManager->persist($storedObject); - try { - $context - ->storeGenerated( - $template, - $storedObject, - $entity, - $contextGenerationData - ); - } catch (Exception $e) { - $this - ->logger - ->error( - 'Unable to store the associated document to entity', - [ - 'entityClassName' => $entityClassName, - 'entityId' => $entityId, - 'contextKey' => $context->getName(), - ] - ); - - throw $e; - } + // we store the generated document + $context + ->storeGenerated( + $template, + $storedObject, + $entity, + $contextGenerationData + ); $this->entityManager->flush(); + $this->messageBus->dispatch( + new RequestGenerationMessage( + $this->getUser(), + $template, + $entityId, + $storedObject, + $contextGenerationDataSanitized, + ) + ); + return $this ->redirectToRoute( 'chill_wopi_file_edit', diff --git a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php index 9f879f7ff..0f62931b4 100644 --- a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php +++ b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php @@ -53,6 +53,7 @@ final class RelatorioDriver implements DriverInterface $response = $this->client->request('POST', $this->url, [ 'headers' => $form->getPreparedHeaders()->toArray(), 'body' => $form->bodyToIterable(), + 'timeout' => '300', ]); return $response->getContent(); diff --git a/src/Bundle/ChillDocGeneratorBundle/Resources/views/Email/on_generation_failed_email.txt.twig b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Email/on_generation_failed_email.txt.twig new file mode 100644 index 000000000..c4ca7079d --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Email/on_generation_failed_email.txt.twig @@ -0,0 +1,16 @@ +{{ creator.label }}, + +{{ 'docgen.failure_email.The generation of the document {template_name} failed'|trans({'{template_name}': template.name|localize_translatable_string}) }} + +{{ 'docgen.failure_email.Forward this email to your administrator for solving'|trans }} + +{{ 'docgen.failure_email.References'|trans }}: +{% if errors|length > 0 %} +{{ 'docgen.failure_email.The following errors were encoutered'|trans }}: + +{% for error in errors %} +- {{ error }} +{% endfor %} +{% endif %} +- template_id: {{ template.id }} +- stored_object_destination_id: {{ stored_object_id }} diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php new file mode 100644 index 000000000..0775cfb49 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/Generator.php @@ -0,0 +1,143 @@ +contextManager = $contextManager; + $this->driver = $driver; + $this->entityManager = $entityManager; + $this->logger = $logger; + $this->storedObjectManager = $storedObjectManager; + } + + /** + * @template T of File|null + * @template B of bool + * @param B $isTest + * @param (B is true ? T : null) $testFile + * @psalm-return (B is true ? string : null) + * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface|\Throwable + */ + public function generateDocFromTemplate( + DocGeneratorTemplate $template, + int $entityId, + array $contextGenerationDataNormalized, + ?StoredObject $destinationStoredObject = null, + bool $isTest = false, + ?File $testFile = null + ): ?string { + if ($destinationStoredObject instanceof StoredObject && StoredObject::STATUS_PENDING !== $destinationStoredObject->getStatus()) { + $this->logger->info(self::LOG_PREFIX.'Aborting generation of an already generated document'); + throw new ObjectReadyException(); + } + + $this->logger->info(self::LOG_PREFIX.'Starting generation of a document', [ + 'entity_id' => $entityId, + 'destination_stored_object' => $destinationStoredObject === null ? null : $destinationStoredObject->getId() + ]); + + $context = $this->contextManager->getContextByDocGeneratorTemplate($template); + + $entity = $this + ->entityManager + ->find($context->getEntityClass(), $entityId) + ; + + if (null === $entity) { + throw new RelatedEntityNotFoundException($template->getEntity(), $entityId); + } + + $contextGenerationDataNormalized = array_merge( + $contextGenerationDataNormalized, + $context instanceof DocGeneratorContextWithPublicFormInterface ? + $context->contextGenerationDataDenormalize($template, $entity, $contextGenerationDataNormalized) + : [] + ); + + $data = $context->getData($template, $entity, $contextGenerationDataNormalized); + + $destinationStoredObjectId = $destinationStoredObject instanceof StoredObject ? $destinationStoredObject->getId() : null; + $this->entityManager->clear(); + gc_collect_cycles(); + if (null !== $destinationStoredObjectId) { + $destinationStoredObject = $this->entityManager->find(StoredObject::class, $destinationStoredObjectId); + } + + if ($isTest && ($testFile instanceof File)) { + $templateDecrypted = file_get_contents($testFile->getPathname()); + } else { + $templateDecrypted = $this->storedObjectManager->read($template->getFile()); + } + + try { + $generatedResource = $this + ->driver + ->generateFromString( + $templateDecrypted, + $template->getFile()->getType(), + $data, + $template->getFile()->getFilename() + ); + } catch (TemplateException $e) { + throw new GeneratorException($e->getErrors(), $e); + } + + if ($isTest) { + $this->logger->info(self::LOG_PREFIX.'Finished generation of a document', [ + 'is_test' => true, + 'entity_id' => $entityId, + 'destination_stored_object' => $destinationStoredObject === null ? null : $destinationStoredObject->getId() + ]); + return $generatedResource; + } + + /** @var StoredObject $storedObject */ + $destinationStoredObject + ->setType($template->getFile()->getType()) + ->setFilename(sprintf('%s_odt', uniqid('doc_', true))) + ->setStatus(StoredObject::STATUS_READY) + ; + + $this->storedObjectManager->write($destinationStoredObject, $generatedResource); + + $this->entityManager->flush(); + + $this->logger->info(self::LOG_PREFIX.'Finished generation of a document', [ + 'entity_id' => $entityId, + 'destination_stored_object' => $destinationStoredObject->getId(), + ]); + + return null; + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php new file mode 100644 index 000000000..0f3412859 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorException.php @@ -0,0 +1,26 @@ + + */ + private array $errors; + + public function __construct(array $errors = [], \Throwable $previous = null) + { + $this->errors = $errors; + parent::__construct("Could not generate the document", 15252, + $previous); + } + + /** + * @return array + */ + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorInterface.php b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorInterface.php new file mode 100644 index 000000000..385e1d010 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/Service/Generator/GeneratorInterface.php @@ -0,0 +1,27 @@ +docGeneratorTemplateRepository = $docGeneratorTemplateRepository; + $this->entityManager = $entityManager; + $this->logger = $logger; + $this->mailer = $mailer; + $this->storedObjectRepository = $storedObjectRepository; + $this->translator = $translator; + $this->userRepository = $userRepository; + } + + + public static function getSubscribedEvents() + { + return [ + WorkerMessageFailedEvent::class => 'onMessageFailed' + ]; + } + + public function onMessageFailed(WorkerMessageFailedEvent $event): void + { + if ($event->willRetry()) { + return; + } + + if (!$event->getEnvelope()->getMessage() instanceof RequestGenerationMessage) { + return; + } + + /** @var \Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage $message */ + $message = $event->getEnvelope()->getMessage(); + + $this->logger->error(self::LOG_PREFIX.'Docgen failed', [ + 'stored_object_id' => $message->getDestinationStoredObjectId(), + 'entity_id' => $message->getEntityId(), + 'template_id' => $message->getTemplateId(), + 'creator_id' => $message->getCreatorId(), + 'throwable_class' => get_class($event->getThrowable()), + ]); + + $this->markObjectAsFailed($message); + $this->warnCreator($message, $event); + } + + private function markObjectAsFailed(RequestGenerationMessage $message): void + { + $object = $this->storedObjectRepository->find($message->getDestinationStoredObjectId()); + + if (null === $object) { + $this->logger->error(self::LOG_PREFIX.'Stored object not found', ['stored_object_id', $message->getDestinationStoredObjectId()]); + } + + $object->setStatus(StoredObject::STATUS_FAILURE); + + $this->entityManager->flush(); + } + + private function warnCreator(RequestGenerationMessage $message, WorkerMessageFailedEvent $event): void + { + if (null === $creatorId = $message->getCreatorId()) { + $this->logger->info(self::LOG_PREFIX.'creator id is null'); + return; + } + + if (null === $creator = $this->userRepository->find($creatorId)) { + $this->logger->error(self::LOG_PREFIX.'Creator not found with given id', ['creator_id', $creatorId]); + return; + } + + if (null === $creator->getEmail() || '' === $creator->getEmail()) { + $this->logger->info(self::LOG_PREFIX.'Creator does not have any email', ['user' => $creator->getUsernameCanonical()]); + return; + } + + // if the exception is not a GeneratorException, we try the previous one... + $throwable = $event->getThrowable(); + if (!$throwable instanceof GeneratorException) { + $throwable = $throwable->getPrevious(); + } + + if ($throwable instanceof GeneratorException) { + $errors = $throwable->getErrors(); + } else { + $errors = [$throwable->getTraceAsString()]; + } + + if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) { + $this->logger->info(self::LOG_PREFIX.'Template not found', ['template_id' => $message->getTemplateId()]); + return; + } + + $email = (new TemplatedEmail()) + ->to($creator->getEmail()) + ->subject($this->translator->trans('docgen.failure_email.The generation of a document failed')) + ->textTemplate('@ChillDocGenerator/Email/on_generation_failed_email.txt.twig') + ->context([ + 'errors' => $errors, + 'template' => $template, + 'creator' => $creator, + 'stored_object_id' => $message->getDestinationStoredObjectId(), + ]); + + $this->mailer->send($email); + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationHandler.php b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationHandler.php new file mode 100644 index 000000000..0cfdecd0a --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationHandler.php @@ -0,0 +1,66 @@ +docGeneratorTemplateRepository = $docGeneratorTemplateRepository; + $this->entityManager = $entityManager; + $this->generator = $generator; + $this->storedObjectRepository = $storedObjectRepository; + } + + public function __invoke(RequestGenerationMessage $message) + { + if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) { + throw new \RuntimeException('template not found: ' . $message->getTemplateId()); + } + + if (null === $destinationStoredObject = $this->storedObjectRepository->find($message->getDestinationStoredObjectId())) { + throw new \RuntimeException('destination stored object not found : ' . $message->getDestinationStoredObjectId()); + } + + if ($destinationStoredObject->getGenerationTrialsCounter() >= self::AUTHORIZED_TRIALS) { + throw new UnrecoverableMessageHandlingException('maximum number of retry reached'); + } + + $destinationStoredObject->addGenerationTrial(); + $this->entityManager->createQuery('UPDATE '.StoredObject::class.' s SET s.generationTrialsCounter = s.generationTrialsCounter + 1 WHERE s.id = :id') + ->setParameter('id', $destinationStoredObject->getId()) + ->execute(); + + $this->generator->generateDocFromTemplate( + $template, + $message->getEntityId(), + $message->getContextGenerationData(), + $destinationStoredObject + ); + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php new file mode 100644 index 000000000..edba90ce4 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/Service/Messenger/RequestGenerationMessage.php @@ -0,0 +1,59 @@ +creatorId = $creator->getId(); + $this->templateId = $template->getId(); + $this->entityId = $entityId; + $this->destinationStoredObjectId = $destinationStoredObject->getId(); + $this->contextGenerationData = $contextGenerationData; + } + + public function getCreatorId(): int + { + return $this->creatorId; + } + + public function getDestinationStoredObjectId(): int + { + return $this->destinationStoredObjectId; + } + + public function getTemplateId(): int + { + return $this->templateId; + } + + public function getEntityId(): int + { + return $this->entityId; + } + + public function getContextGenerationData(): array + { + return $this->contextGenerationData; + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/config/services.yaml b/src/Bundle/ChillDocGeneratorBundle/config/services.yaml index 5bdfe2a11..5fef6fb22 100644 --- a/src/Bundle/ChillDocGeneratorBundle/config/services.yaml +++ b/src/Bundle/ChillDocGeneratorBundle/config/services.yaml @@ -20,10 +20,14 @@ services: resource: '../Serializer/Normalizer/' tags: - { name: 'serializer.normalizer', priority: -152 } + Chill\DocGeneratorBundle\Serializer\Normalizer\CollectionDocGenNormalizer: tags: - { name: 'serializer.normalizer', priority: -126 } + Chill\DocGeneratorBundle\Service\Context\: + resource: "../Service/Context" + Chill\DocGeneratorBundle\Controller\: resource: "../Controller" autowire: true @@ -34,18 +38,20 @@ services: autowire: true autoconfigure: true - Chill\DocGeneratorBundle\Service\Context\: - resource: "../Service/Context/" - autowire: true - autoconfigure: true - Chill\DocGeneratorBundle\GeneratorDriver\: resource: "../GeneratorDriver/" autowire: true autoconfigure: true + Chill\DocGeneratorBundle\Service\Messenger\: + resource: "../Service/Messenger/" + + Chill\DocGeneratorBundle\Service\Generator\Generator: ~ + Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface: '@Chill\DocGeneratorBundle\Service\Generator\Generator' + Chill\DocGeneratorBundle\Driver\RelatorioDriver: '@Chill\DocGeneratorBundle\Driver\DriverInterface' Chill\DocGeneratorBundle\Context\ContextManager: arguments: $contexts: !tagged_iterator { tag: chill_docgen.context, default_index_method: getKey } + Chill\DocGeneratorBundle\Context\ContextManagerInterface: '@Chill\DocGeneratorBundle\Context\ContextManager' diff --git a/src/Bundle/ChillDocGeneratorBundle/migrations/Version20230214192558.php b/src/Bundle/ChillDocGeneratorBundle/migrations/Version20230214192558.php new file mode 100644 index 000000000..de536ca84 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/migrations/Version20230214192558.php @@ -0,0 +1,47 @@ +addSql('ALTER TABLE chill_doc.stored_object ADD template_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_doc.stored_object ADD status TEXT DEFAULT \'ready\' NOT NULL'); + $this->addSql('ALTER TABLE chill_doc.stored_object ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('UPDATE chill_doc.stored_object SET createdAt = creation_date'); + $this->addSql('ALTER TABLE chill_doc.stored_object ADD createdBy_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_doc.stored_object DROP creation_date;'); + $this->addSql('ALTER TABLE chill_doc.stored_object ALTER type SET DEFAULT \'\''); + $this->addSql('ALTER TABLE chill_doc.stored_object ALTER title DROP DEFAULT'); + $this->addSql('COMMENT ON COLUMN chill_doc.stored_object.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_doc.stored_object ADD CONSTRAINT FK_49604E365DA0FB8 FOREIGN KEY (template_id) REFERENCES chill_docgen_template (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_doc.stored_object ADD CONSTRAINT FK_49604E363174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_49604E365DA0FB8 ON chill_doc.stored_object (template_id)'); + $this->addSql('CREATE INDEX IDX_49604E363174800F ON chill_doc.stored_object (createdBy_id)'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_doc.stored_object DROP CONSTRAINT FK_49604E365DA0FB8'); + $this->addSql('ALTER TABLE chill_doc.stored_object DROP CONSTRAINT FK_49604E363174800F'); + $this->addSql('ALTER TABLE chill_doc.stored_object DROP template_id'); + $this->addSql('ALTER TABLE chill_doc.stored_object DROP status'); + $this->addSql('ALTER TABLE chill_doc.stored_object ADD creation_date TIMESTAMP(0) DEFAULT NOW()'); + $this->addSql('UPDATE chill_doc.stored_object SET creation_date = createdAt'); + $this->addSql('ALTER TABLE chill_doc.stored_object DROP createdAt'); + $this->addSql('ALTER TABLE chill_doc.stored_object DROP createdBy_id'); + $this->addSql('ALTER TABLE chill_doc.stored_object ALTER title SET DEFAULT \'\''); + $this->addSql('ALTER TABLE chill_doc.stored_object ALTER type DROP DEFAULT'); + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php b/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php new file mode 100644 index 000000000..066715b60 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/Generator/GeneratorTest.php @@ -0,0 +1,141 @@ +setFile($templateStoredObject = (new StoredObject()) + ->setType('application/test')); + $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING); + $reflection = new \ReflectionClass($destinationStoredObject); + $reflection->getProperty('id')->setAccessible(true); + $reflection->getProperty('id')->setValue($destinationStoredObject, 1); + $entity = new class {}; + $data = []; + + $context = $this->prophesize(DocGeneratorContextInterface::class); + $context->getData($template, $entity, Argument::type('array'))->willReturn($data); + $context->getName()->willReturn('dummy_context'); + $context->getEntityClass()->willReturn('DummyClass'); + $context = $context->reveal(); + + $contextManagerInterface = $this->prophesize(ContextManagerInterface::class); + $contextManagerInterface->getContextByDocGeneratorTemplate($template) + ->willReturn($context); + + $driver = $this->prophesize(DriverInterface::class); + $driver->generateFromString('template', 'application/test', $data, Argument::any()) + ->willReturn('generated'); + + $entityManager = $this->prophesize(EntityManagerInterface::class); + $entityManager->find(StoredObject::class, 1) + ->willReturn($destinationStoredObject); + $entityManager->find('DummyClass', Argument::type('int')) + ->willReturn($entity); + $entityManager->clear()->shouldBeCalled(); + $entityManager->flush()->shouldBeCalled(); + + $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class); + $storedObjectManager->read($templateStoredObject)->willReturn('template'); + $storedObjectManager->write($destinationStoredObject, 'generated')->shouldBeCalled(); + + + $generator = new Generator( + $contextManagerInterface->reveal(), + $driver->reveal(), + $entityManager->reveal(), + new NullLogger(), + $storedObjectManager->reveal() + ); + + $generator->generateDocFromTemplate( + $template, + 1, + [], + $destinationStoredObject + ); + } + + public function testPreventRegenerateDocument(): void + { + $this->expectException(ObjectReadyException::class); + + $generator = new Generator( + $this->prophesize(ContextManagerInterface::class)->reveal(), + $this->prophesize(DriverInterface::class)->reveal(), + $this->prophesize(EntityManagerInterface::class)->reveal(), + new NullLogger(), + $this->prophesize(StoredObjectManagerInterface::class)->reveal() + ); + + $template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject()) + ->setType('application/test')); + $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_READY); + + $generator->generateDocFromTemplate( + $template, + 1, + [], + $destinationStoredObject + ); + } + + public function testRelatedEntityNotFound(): void + { + $this->expectException(RelatedEntityNotFoundException::class); + + $template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject()) + ->setType('application/test')); + $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING); + $reflection = new \ReflectionClass($destinationStoredObject); + $reflection->getProperty('id')->setAccessible(true); + $reflection->getProperty('id')->setValue($destinationStoredObject, 1); + + $context = $this->prophesize(DocGeneratorContextInterface::class); + $context->getName()->willReturn('dummy_context'); + $context->getEntityClass()->willReturn('DummyClass'); + $context = $context->reveal(); + + $contextManagerInterface = $this->prophesize(ContextManagerInterface::class); + $contextManagerInterface->getContextByDocGeneratorTemplate($template) + ->willReturn($context); + + $entityManager = $this->prophesize(EntityManagerInterface::class); + $entityManager->find(Argument::type('string'), Argument::type('int')) + ->willReturn(null); + + $generator = new Generator( + $contextManagerInterface->reveal(), + $this->prophesize(DriverInterface::class)->reveal(), + $entityManager->reveal(), + new NullLogger(), + $this->prophesize(StoredObjectManagerInterface::class)->reveal() + ); + + $generator->generateDocFromTemplate( + $template, + 1, + [], + $destinationStoredObject + ); + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml b/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml index bf37ff838..d0950482e 100644 --- a/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml @@ -10,6 +10,16 @@ docgen: test generate: Tester la génération With context %name%: 'Avec le contexte "%name%"' + Doc generation failed: La génération de ce document a échoué + Doc generation is pending: La génération de ce document est en cours + Come back later: Revenir plus tard + + failure_email: + The generation of a document failed: La génération d'un document a échoué + The generation of the document {template_name} failed: La génération d'un document à partir du modèle {{ template_name }} a échoué. + The following errors were encoutered: Les erreurs suivantes ont été rencontrées + Forward this email to your administrator for solving: Faites suivre ce message vers votre administrateur pour la résolution du problème. + References: Références crud: docgen_template: @@ -19,4 +29,4 @@ crud: Show data instead of generating: Montrer les données au lieu de générer le document -Template file: Fichier modèle \ No newline at end of file +Template file: Fichier modèle diff --git a/src/Bundle/ChillDocStoreBundle/Controller/StoredObjectApiController.php b/src/Bundle/ChillDocStoreBundle/Controller/StoredObjectApiController.php new file mode 100644 index 000000000..29b978ffb --- /dev/null +++ b/src/Bundle/ChillDocStoreBundle/Controller/StoredObjectApiController.php @@ -0,0 +1,39 @@ +security = $security; + } + + /** + * @Route("/api/1.0/doc-store/stored-object/{uuid}/is-ready") + */ + public function isDocumentReady(StoredObject $storedObject): Response + { + if (!$this->security->isGranted('ROLE_USER')) { + throw new AccessDeniedHttpException(); + } + + return new JsonResponse( + [ + 'id' => $storedObject->getId(), + 'filename' => $storedObject->getFilename(), + 'status' => $storedObject->getStatus(), + 'type' => $storedObject->getType(), + ] + ); + } +} diff --git a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php index ec2b67cf2..8821ead7c 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php @@ -14,6 +14,9 @@ namespace Chill\DocStoreBundle\Entity; use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface; use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists; use ChampsLibres\WopiLib\Contract\Entity\Document; +use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; +use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; +use Chill\MainBundle\Doctrine\Model\TrackCreationTrait; use DateTime; use DateTimeInterface; use Doctrine\ORM\Mapping as ORM; @@ -30,13 +33,13 @@ use Symfony\Component\Serializer\Annotation as Serializer; * message="The file is not stored properly" * ) */ -class StoredObject implements AsyncFileInterface, Document +class StoredObject implements AsyncFileInterface, Document, TrackCreationInterface { - /** - * @ORM\Column(type="datetime", name="creation_date") - * @Serializer\Groups({"read", "write"}) - */ - private DateTimeInterface $creationDate; + public const STATUS_READY = "ready"; + public const STATUS_PENDING = "pending"; + public const STATUS_FAILURE = "failure"; + + use TrackCreationTrait; /** * @ORM\Column(type="json", name="datas") @@ -48,7 +51,7 @@ class StoredObject implements AsyncFileInterface, Document * @ORM\Column(type="text") * @Serializer\Groups({"read", "write"}) */ - private $filename; + private string $filename = ''; /** * @ORM\Id @@ -56,7 +59,7 @@ class StoredObject implements AsyncFileInterface, Document * @ORM\Column(type="integer") * @Serializer\Groups({"read", "write"}) */ - private $id; + private ?int $id; /** * @var int[] @@ -78,7 +81,7 @@ class StoredObject implements AsyncFileInterface, Document private string $title = ''; /** - * @ORM\Column(type="text", name="type") + * @ORM\Column(type="text", name="type", options={"default": ""}) * @Serializer\Groups({"read", "write"}) */ private string $type = ''; @@ -89,28 +92,68 @@ class StoredObject implements AsyncFileInterface, Document */ private UuidInterface $uuid; - public function __construct() + /** + * @ORM\ManyToOne(targetEntity=DocGeneratorTemplate::class) + */ + private ?DocGeneratorTemplate $template; + + /** + * @ORM\Column(type="text", options={"default": "ready"}) + * @Serializer\Groups({"read"}) + */ + private string $status; + + /** + * Store the number of times a generation has been tryied for this StoredObject. + * + * This is a workaround, as generation consume lot of memory, and out-of-memory errors + * are not handled by messenger. + * + * @ORM\Column(type="integer", options={"default": 0}) + */ + private int $generationTrialsCounter = 0; + + /** + * @param StoredObject::STATUS_* $status + */ + public function __construct(string $status = "ready") { - $this->creationDate = new DateTime(); $this->uuid = Uuid::uuid4(); + $this->status = $status; } + public function addGenerationTrial(): self + { + $this->generationTrialsCounter++; + + return $this; + } + + /** + * @Serializer\Groups({"read", "write"}) + * @deprecated + */ public function getCreationDate(): DateTime { - return $this->creationDate; + return DateTime::createFromImmutable($this->createdAt); } - public function getDatas() + public function getDatas(): array { return $this->datas; } - public function getFilename() + public function getFilename(): string { return $this->filename; } - public function getId() + public function getGenerationTrialsCounter(): int + { + return $this->generationTrialsCounter; + } + + public function getId(): ?int { return $this->id; } @@ -133,6 +176,14 @@ class StoredObject implements AsyncFileInterface, Document return $this->getFilename(); } + /** + * @return StoredObject::STATUS_* + */ + public function getStatus(): string + { + return $this->status; + } + public function getTitle() { return $this->title; @@ -153,52 +204,92 @@ class StoredObject implements AsyncFileInterface, Document return (string) $this->uuid; } - public function setCreationDate(DateTime $creationDate) + /** + * @Serializer\Groups({"write"}) + * @deprecated + */ + public function setCreationDate(DateTime $creationDate): self { - $this->creationDate = $creationDate; + $this->createdAt = \DateTimeImmutable::createFromMutable($creationDate); return $this; } - public function setDatas(?array $datas) + public function setDatas(?array $datas): self { $this->datas = (array) $datas; return $this; } - public function setFilename(?string $filename) + public function setFilename(?string $filename): self { $this->filename = (string) $filename; return $this; } - public function setIv(?array $iv) + public function setIv(?array $iv): self { $this->iv = (array) $iv; return $this; } - public function setKeyInfos(?array $keyInfos) + public function setKeyInfos(?array $keyInfos): self { $this->keyInfos = (array) $keyInfos; return $this; } - public function setTitle(?string $title) + /** + * @param StoredObject::STATUS_* $status + */ + public function setStatus(string $status): self + { + $this->status = $status; + + return $this; + } + + public function setTitle(?string $title): self { $this->title = (string) $title; return $this; } - public function setType(?string $type) + public function setType(?string $type): self { $this->type = (string) $type; return $this; } + + public function getTemplate(): ?DocGeneratorTemplate + { + return $this->template; + } + + public function hasTemplate(): bool + { + return null !== $this->template; + } + + public function setTemplate(?DocGeneratorTemplate $template): StoredObject + { + $this->template = $template; + return $this; + } + + public function isPending(): bool + { + return self::STATUS_PENDING === $this->getStatus(); + } + + public function isFailure(): bool + { + return self::STATUS_FAILURE === $this->getStatus(); + } } diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts index ec1d50a86..e5912d38a 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts @@ -1,7 +1,8 @@ import {_createI18n} from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n"; import DocumentActionButtonsGroup from "../../vuejs/DocumentActionButtonsGroup.vue"; import {createApp} from "vue"; -import {StoredObject} from "../../types"; +import {StoredObject, StoredObjectStatusChange} from "../../types"; +import {is_object_ready} from "../../vuejs/StoredObjectButton/helpers"; const i18n = _createI18n({}); @@ -19,7 +20,7 @@ window.addEventListener('DOMContentLoaded', function (e) { }; const - storedObject = JSON.parse(datasets.storedObject), + storedObject = JSON.parse(datasets.storedObject) as StoredObject, filename = datasets.filename, canEdit = datasets.canEdit === '1', small = datasets.small === '1' @@ -27,7 +28,20 @@ window.addEventListener('DOMContentLoaded', function (e) { return { storedObject, filename, canEdit, small }; }, - template: '', + template: '', + methods: { + onStoredObjectStatusChange: function(newStatus: StoredObjectStatusChange): void { + this.$data.storedObject.status = newStatus.status; + this.$data.storedObject.filename = newStatus.filename; + this.$data.storedObject.type = newStatus.type; + + // remove eventual div which inform pending status + document.querySelectorAll(`[data-docgen-is-pending="${this.$data.storedObject.id}"]`) + .forEach(function(el) { + el.remove(); + }); + } + } }); app.use(i18n).mount(el); diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts index 918526117..825055973 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/types.ts @@ -1,5 +1,7 @@ import {DateTime} from "../../../ChillMainBundle/Resources/public/types"; +export type StoredObjectStatus = "ready"|"failure"|"pending"; + export interface StoredObject { id: number, @@ -13,7 +15,15 @@ export interface StoredObject { keyInfos: object, title: string, type: string, - uuid: string + uuid: string, + status: StoredObjectStatus, +} + +export interface StoredObjectStatusChange { + id: number, + filename: string, + status: StoredObjectStatus, + type: string, } /** diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue index 60b368cd3..ac841f5cf 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue @@ -1,5 +1,5 @@