Merge branch '324-convert-to-pdf-on-signature-only' into 'master'

Lors de la procédure de signature, le document ne doit être converti qu'à partir du moment où la première signature est apposée

Closes #324

See merge request Chill-Projet/chill-bundles!758
This commit is contained in:
Julien Fastré 2024-11-15 13:33:09 +00:00
commit f04ef3c3e3
12 changed files with 91 additions and 299 deletions

View File

@ -15,12 +15,14 @@ use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessa
use Chill\DocStoreBundle\Service\Signature\PDFPage; use Chill\DocStoreBundle\Service\Signature\PDFPage;
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone; use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\DocStoreBundle\Service\StoredObjectToPdfConverter;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum; use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderManagerInterface; use Chill\MainBundle\Templating\Entity\ChillEntityRenderManagerInterface;
use Chill\MainBundle\Security\Authorization\EntityWorkflowStepSignatureVoter; use Chill\MainBundle\Security\Authorization\EntityWorkflowStepSignatureVoter;
use Chill\MainBundle\Templating\Entity\UserRender; use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -39,6 +41,8 @@ class SignatureRequestController
private readonly ChillEntityRenderManagerInterface $entityRender, private readonly ChillEntityRenderManagerInterface $entityRender,
private readonly NormalizerInterface $normalizer, private readonly NormalizerInterface $normalizer,
private readonly Security $security, private readonly Security $security,
private readonly StoredObjectToPdfConverter $converter,
private readonly EntityManagerInterface $entityManager,
) {} ) {}
#[Route('/api/1.0/document/workflow/{id}/signature-request', name: 'chill_docstore_signature_request')] #[Route('/api/1.0/document/workflow/{id}/signature-request', name: 'chill_docstore_signature_request')]
@ -53,11 +57,17 @@ class SignatureRequestController
if (EntityWorkflowSignatureStateEnum::PENDING !== $signature->getState()) { if (EntityWorkflowSignatureStateEnum::PENDING !== $signature->getState()) {
return new JsonResponse([], status: Response::HTTP_CONFLICT); return new JsonResponse([], status: Response::HTTP_CONFLICT);
} }
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow); $storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
$content = $this->storedObjectManager->read($storedObject);
$data = \json_decode((string) $request->getContent(), true, 512, JSON_THROW_ON_ERROR); // TODO parse payload: json_decode ou, mieux, dataTransfertObject if ('application/pdf' !== $storedObject->getType()) {
[$storedObject, $storedObjectVersion, $content] = $this->converter->addConvertedVersion($storedObject, $request->getLocale(), includeConvertedContent: true);
$this->entityManager->persist($storedObjectVersion);
$this->entityManager->flush();
} else {
$content = $this->storedObjectManager->read($storedObject);
}
$data = \json_decode((string) $request->getContent(), true, 512, JSON_THROW_ON_ERROR);
$zone = new PDFSignatureZone( $zone = new PDFSignatureZone(
$data['zone']['index'], $data['zone']['index'],
$data['zone']['x'], $data['zone']['x'],

View File

@ -277,7 +277,7 @@ console.log(PdfWorker); // incredible but this is needed
// pdfjsLib.GlobalWorkerOptions.workerSrc = PdfWorker; // pdfjsLib.GlobalWorkerOptions.workerSrc = PdfWorker;
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue"; import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { download_and_decrypt_doc } from "../StoredObjectButton/helpers"; import {download_and_decrypt_doc, download_doc_as_pdf} from "../StoredObjectButton/helpers";
pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs"; pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";
@ -385,10 +385,7 @@ const init = () => downloadAndOpen().then(initPdf);
async function downloadAndOpen(): Promise<Blob> { async function downloadAndOpen(): Promise<Blob> {
let raw; let raw;
try { try {
raw = await download_and_decrypt_doc( raw = await download_doc_as_pdf(signature.storedObject);
signature.storedObject,
signature.storedObject.currentVersion
);
} catch (e) { } catch (e) {
console.error("error while downloading and decrypting document", e); console.error("error while downloading and decrypting document", e);
throw e; throw e;

View File

@ -3,7 +3,7 @@
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
<template v-if="displayActionStringInButton">Télécharger</template> <template v-if="displayActionStringInButton">Télécharger</template>
</a> </a>
<a v-else :class="props.classes" target="_blank" :type="props.storedObject.type" :download="buildDocumentName()" :href="state.href_url" ref="open_button" title="Ouvrir"> <a v-else :class="props.classes" target="_blank" :type="props.atVersion.type" :download="buildDocumentName()" :href="state.href_url" ref="open_button" title="Ouvrir">
<i class="fa fa-external-link"></i> <i class="fa fa-external-link"></i>
<template v-if="displayActionStringInButton">Ouvrir</template> <template v-if="displayActionStringInButton">Ouvrir</template>
</a> </a>

View File

@ -55,7 +55,7 @@ const classes = computed<{row: true, 'row-hover': true, 'blinking-1': boolean, '
<restore-version-button :stored-object-version="props.version" @restore-version="onRestore"></restore-version-button> <restore-version-button :stored-object-version="props.version" @restore-version="onRestore"></restore-version-button>
</li> </li>
<li> <li>
<download-button :stored-object="storedObject" :at-version="version" :classes="{btn: true, 'btn-outline-primary': true}" :display-action-string-in-button="false"></download-button> <download-button :stored-object="storedObject" :at-version="version" :classes="{btn: true, 'btn-outline-primary': true, 'btn-sm': true}" :display-action-string-in-button="false"></download-button>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -197,6 +197,32 @@ async function download_and_decrypt_doc(storedObject: StoredObject, atVersion: n
} }
} }
/**
* Fetch the stored object as a pdf.
*
* If the document is already in a pdf on the server side, the document is retrieved "as is" from the usual
* storage.
*/
async function download_doc_as_pdf(storedObject: StoredObject): Promise<Blob>
{
if (null === storedObject.currentVersion) {
throw new Error("the stored object does not count any version");
}
if (storedObject.currentVersion?.type === 'application/pdf') {
return download_and_decrypt_doc(storedObject, storedObject.currentVersion);
}
const convertLink = build_convert_link(storedObject.uuid);
const response = await fetch(convertLink);
if (!response.ok) {
throw new Error("Could not convert the document: " + response.status);
}
return response.blob();
}
async function is_object_ready(storedObject: StoredObject): Promise<StoredObjectStatusChange> async function is_object_ready(storedObject: StoredObject): Promise<StoredObjectStatusChange>
{ {
const new_status_response = await window const new_status_response = await window
@ -214,6 +240,7 @@ export {
build_wopi_editor_link, build_wopi_editor_link,
download_and_decrypt_doc, download_and_decrypt_doc,
download_doc, download_doc,
download_doc_as_pdf,
is_extension_editable, is_extension_editable,
is_extension_viewable, is_extension_viewable,
is_object_ready, is_object_ready,

View File

@ -17,15 +17,30 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\WopiBundle\Service\WopiConverter;
use Symfony\Contracts\Translation\LocaleAwareInterface;
class PDFSignatureZoneAvailable class PDFSignatureZoneAvailable implements LocaleAwareInterface
{ {
private string $locale;
public function __construct( public function __construct(
private readonly EntityWorkflowManager $entityWorkflowManager, private readonly EntityWorkflowManager $entityWorkflowManager,
private readonly PDFSignatureZoneParser $pdfSignatureZoneParser, private readonly PDFSignatureZoneParser $pdfSignatureZoneParser,
private readonly StoredObjectManagerInterface $storedObjectManager, private readonly StoredObjectManagerInterface $storedObjectManager,
private readonly WopiConverter $converter,
) {} ) {}
public function setLocale(string $locale)
{
$this->locale = $locale;
}
public function getLocale()
{
return $this->locale;
}
/** /**
* @return list<PDFSignatureZone> * @return list<PDFSignatureZone>
*/ */
@ -38,10 +53,16 @@ class PDFSignatureZoneAvailable
} }
if ('application/pdf' !== $storedObject->getType()) { if ('application/pdf' !== $storedObject->getType()) {
throw new \RuntimeException('Only PDF documents are supported'); $content = $this->converter->convert($this->getLocale(), $this->storedObjectManager->read($storedObject), $storedObject->getType());
} else {
$content = $this->storedObjectManager->read($storedObject);
} }
$zones = $this->pdfSignatureZoneParser->findSignatureZones($this->storedObjectManager->read($storedObject)); $zones = $this->pdfSignatureZoneParser->findSignatureZones($content);
// free some memory as soon as possible...
unset($content);
$signatureZonesIndexes = array_map( $signatureZonesIndexes = array_map(
fn (EntityWorkflowStepSignature $step) => $step->getZoneSignatureIndex(), fn (EntityWorkflowStepSignature $step) => $step->getZoneSignatureIndex(),
$this->collectSignaturesInUse($entityWorkflow) $this->collectSignaturesInUse($entityWorkflow)

View File

@ -39,13 +39,13 @@ class StoredObjectToPdfConverter
* @param string $lang the language for the conversion context * @param string $lang the language for the conversion context
* @param string $convertTo The target format for the conversion. Default is 'pdf'. * @param string $convertTo The target format for the conversion. Default is 'pdf'.
* *
* @return array{0: StoredObjectPointInTime, 1: StoredObjectVersion} contains the point in time before conversion and the new version of the stored object * @return array{0: StoredObjectPointInTime, 1: StoredObjectVersion, 2?: string} contains the point in time before conversion and the new version of the stored object. The converted content is included in the response if $includeConvertedContent is true
* *
* @throws \UnexpectedValueException if the preferred mime type for the conversion is not found * @throws \UnexpectedValueException if the preferred mime type for the conversion is not found
* @throws \RuntimeException if the conversion or storage of the new version fails * @throws \RuntimeException if the conversion or storage of the new version fails
* @throws StoredObjectManagerException * @throws StoredObjectManagerException
*/ */
public function addConvertedVersion(StoredObject $storedObject, string $lang, $convertTo = 'pdf'): array public function addConvertedVersion(StoredObject $storedObject, string $lang, $convertTo = 'pdf', bool $includeConvertedContent = false): array
{ {
$newMimeType = $this->mimeTypes->getMimeTypes($convertTo)[0] ?? null; $newMimeType = $this->mimeTypes->getMimeTypes($convertTo)[0] ?? null;
@ -70,6 +70,11 @@ class StoredObjectToPdfConverter
$pointInTime = new StoredObjectPointInTime($currentVersion, StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION); $pointInTime = new StoredObjectPointInTime($currentVersion, StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION);
$version = $this->storedObjectManager->write($storedObject, $converted, $newMimeType); $version = $this->storedObjectManager->write($storedObject, $converted, $newMimeType);
if (!$includeConvertedContent) {
return [$pointInTime, $version]; return [$pointInTime, $version];
} }
return [$pointInTime, $version, $converted];
}
} }

View File

@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO; use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\WopiBundle\Service\WopiConverter;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Clock\MockClock; use Symfony\Component\Clock\MockClock;
@ -65,6 +66,7 @@ class PDFSignatureZoneAvailableTest extends TestCase
$entityWorkflowManager->reveal(), $entityWorkflowManager->reveal(),
$parser->reveal(), $parser->reveal(),
$storedObjectManager->reveal(), $storedObjectManager->reveal(),
$this->prophesize(WopiConverter::class)->reveal(),
); );
$actual = $filter->getAvailableSignatureZones($entityWorkflow); $actual = $filter->getAvailableSignatureZones($entityWorkflow);

View File

@ -1,208 +0,0 @@
<?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\Workflow;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectPointInTime;
use Chill\DocStoreBundle\Entity\StoredObjectPointInTimeReasonEnum;
use Chill\DocStoreBundle\Service\StoredObjectToPdfConverter;
use Chill\DocStoreBundle\Workflow\ConvertToPdfBeforeSignatureStepEventSubscriber;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Workflow\DefinitionBuilder;
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Workflow\WorkflowInterface;
/**
* @internal
*
* @coversNothing
*/
class ConvertToPdfBeforeSignatureStepEventSubscriberTest extends \PHPUnit\Framework\TestCase
{
use ProphecyTrait;
public function testConvertToPdfBeforeSignatureStepEventSubscriberToSignature(): void
{
$entityWorkflow = new EntityWorkflow();
$storedObject = new StoredObject();
$previousVersion = $storedObject->registerVersion();
$converter = $this->prophesize(StoredObjectToPdfConverter::class);
$converter->addConvertedVersion($storedObject, 'fr', 'pdf')
->shouldBeCalledOnce()
->will(function ($args) {
/** @var StoredObject $storedObject */
$storedObject = $args[0];
$pointInTime = new StoredObjectPointInTime($storedObject->getCurrentVersion(), StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION);
$newVersion = $storedObject->registerVersion(filename: 'next');
return [$pointInTime, $newVersion];
});
$entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
$entityWorkflowManager->getAssociatedStoredObject($entityWorkflow)->willReturn($storedObject);
$request = new Request();
$request->setLocale('fr');
$stack = new RequestStack();
$stack->push($request);
$eventSubscriber = new ConvertToPdfBeforeSignatureStepEventSubscriber($entityWorkflowManager->reveal(), $converter->reveal(), $stack);
$registry = $this->buildRegistry($eventSubscriber);
$workflow = $registry->get($entityWorkflow, 'dummy');
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
$workflow->apply($entityWorkflow, 'to_signature', ['context' => $dto, 'transition' => 'to_signature', 'transitionAt' => new \DateTimeImmutable('now')]);
self::assertEquals('signature', $entityWorkflow->getStep());
self::assertNotSame($previousVersion, $storedObject->getCurrentVersion());
self::assertTrue($previousVersion->hasPointInTimes());
self::assertCount(2, $storedObject->getVersions());
self::assertEquals('next', $storedObject->getCurrentVersion()->getFilename());
}
public function testConvertToPdfBeforeSignatureStepEventSubscriberToNotASignatureStep(): void
{
$entityWorkflow = new EntityWorkflow();
$storedObject = new StoredObject();
$previousVersion = $storedObject->registerVersion();
$converter = $this->prophesize(StoredObjectToPdfConverter::class);
$converter->addConvertedVersion($storedObject, 'fr', 'pdf')
->shouldNotBeCalled();
$entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
$entityWorkflowManager->getAssociatedStoredObject($entityWorkflow)->willReturn($storedObject);
$request = new Request();
$request->setLocale('fr');
$stack = new RequestStack();
$stack->push($request);
$eventSubscriber = new ConvertToPdfBeforeSignatureStepEventSubscriber($entityWorkflowManager->reveal(), $converter->reveal(), $stack);
$registry = $this->buildRegistry($eventSubscriber);
$workflow = $registry->get($entityWorkflow, 'dummy');
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
$workflow->apply($entityWorkflow, 'to_something', ['context' => $dto, 'transition' => 'to_something', 'transitionAt' => new \DateTimeImmutable('now')]);
self::assertEquals('something', $entityWorkflow->getStep());
self::assertSame($previousVersion, $storedObject->getCurrentVersion());
self::assertFalse($previousVersion->hasPointInTimes());
self::assertCount(1, $storedObject->getVersions());
}
public function testConvertToPdfBeforeSignatureStepEventSubscriberToSignatureAlreadyAPdf(): void
{
$entityWorkflow = new EntityWorkflow();
$storedObject = new StoredObject();
$previousVersion = $storedObject->registerVersion(type: 'application/pdf');
$converter = $this->prophesize(StoredObjectToPdfConverter::class);
$converter->addConvertedVersion($storedObject, 'fr', 'pdf')
->shouldNotBeCalled();
$entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
$entityWorkflowManager->getAssociatedStoredObject($entityWorkflow)->willReturn($storedObject);
$request = new Request();
$request->setLocale('fr');
$stack = new RequestStack();
$stack->push($request);
$eventSubscriber = new ConvertToPdfBeforeSignatureStepEventSubscriber($entityWorkflowManager->reveal(), $converter->reveal(), $stack);
$registry = $this->buildRegistry($eventSubscriber);
$workflow = $registry->get($entityWorkflow, 'dummy');
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
$workflow->apply($entityWorkflow, 'to_signature', ['context' => $dto, 'transition' => 'to_signature', 'transitionAt' => new \DateTimeImmutable('now')]);
self::assertEquals('signature', $entityWorkflow->getStep());
self::assertSame($previousVersion, $storedObject->getCurrentVersion());
self::assertFalse($previousVersion->hasPointInTimes());
self::assertCount(1, $storedObject->getVersions());
}
public function testConvertToPdfBeforeSignatureStepEventSubscriberToSignatureWithNoStoredObject(): void
{
$entityWorkflow = new EntityWorkflow();
$converter = $this->prophesize(StoredObjectToPdfConverter::class);
$converter->addConvertedVersion(Argument::type(StoredObject::class), 'fr', 'pdf')
->shouldNotBeCalled();
$entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
$entityWorkflowManager->getAssociatedStoredObject($entityWorkflow)->willReturn(null);
$request = new Request();
$request->setLocale('fr');
$stack = new RequestStack();
$stack->push($request);
$eventSubscriber = new ConvertToPdfBeforeSignatureStepEventSubscriber($entityWorkflowManager->reveal(), $converter->reveal(), $stack);
$registry = $this->buildRegistry($eventSubscriber);
$workflow = $registry->get($entityWorkflow, 'dummy');
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
$workflow->apply($entityWorkflow, 'to_signature', ['context' => $dto, 'transition' => 'to_signature', 'transitionAt' => new \DateTimeImmutable('now')]);
self::assertEquals('signature', $entityWorkflow->getStep());
}
private function buildRegistry(EventSubscriberInterface $eventSubscriber): Registry
{
$builder = new DefinitionBuilder();
$builder
->setInitialPlaces('initial')
->addPlaces(['initial', 'signature', 'something'])
->addTransition(new Transition('to_something', 'initial', 'something'))
->addTransition(new Transition('to_signature', 'initial', 'signature'));
$metadataStore = new InMemoryMetadataStore([], ['signature' => ['isSignature' => ['user']]]);
$builder->setMetadataStore($metadataStore);
$eventDispatcher = new EventDispatcher();
$eventDispatcher->addSubscriber($eventSubscriber);
$workflow = new Workflow($builder->build(), new EntityWorkflowMarkingStore(), $eventDispatcher, 'dummy');
$supports = new class () implements WorkflowSupportStrategyInterface {
public function supports(WorkflowInterface $workflow, object $subject): bool
{
return true;
}
};
$registry = new Registry();
$registry->addWorkflow($workflow, $supports);
return $registry;
}
}

View File

@ -1,75 +0,0 @@
<?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\Workflow;
use Chill\DocStoreBundle\Service\StoredObjectToPdfConverter;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Workflow\Event\CompletedEvent;
use Symfony\Component\Workflow\WorkflowEvents;
/**
* Event subscriber to convert objects to PDF when the document reach a signature step.
*/
class ConvertToPdfBeforeSignatureStepEventSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly EntityWorkflowManager $entityWorkflowManager,
private readonly StoredObjectToPdfConverter $storedObjectToPdfConverter,
private readonly RequestStack $requestStack,
) {}
public static function getSubscribedEvents(): array
{
return [
WorkflowEvents::COMPLETED => 'convertToPdfBeforeSignatureStepEvent',
];
}
public function convertToPdfBeforeSignatureStepEvent(CompletedEvent $event): void
{
$entityWorkflow = $event->getSubject();
if (!$entityWorkflow instanceof EntityWorkflow) {
return;
}
$tos = $event->getTransition()->getTos();
$workflow = $event->getWorkflow();
$metadataStore = $workflow->getMetadataStore();
foreach ($tos as $to) {
$metadata = $metadataStore->getPlaceMetadata($to);
if (array_key_exists('isSignature', $metadata) && 0 < count($metadata['isSignature'])) {
$this->convertToPdf($entityWorkflow);
return;
}
}
}
private function convertToPdf(EntityWorkflow $entityWorkflow): void
{
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
if (null === $storedObject) {
return;
}
if ('application/pdf' === $storedObject->getCurrentVersion()->getType()) {
return;
}
$this->storedObjectToPdfConverter->addConvertedVersion($storedObject, $this->requestStack->getCurrentRequest()->getLocale(), 'pdf');
}
}

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\WopiBundle\Controller; namespace Chill\WopiBundle\Controller;
use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Service\StoredObjectManager; use Chill\DocStoreBundle\Service\StoredObjectManager;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\WopiBundle\Service\WopiConverter; use Chill\WopiBundle\Service\WopiConverter;
@ -41,7 +42,16 @@ class ConvertController
throw new AccessDeniedHttpException('User must be authenticated'); throw new AccessDeniedHttpException('User must be authenticated');
} }
if (!$this->security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)) {
throw new AccessDeniedHttpException('not allowed to see this document');
}
$content = $this->storedObjectManager->read($storedObject); $content = $this->storedObjectManager->read($storedObject);
if ('application/pdf' === $storedObject->getType()) {
return new Response($content, Response::HTTP_OK, ['Content-Type' => 'application/pdf']);
}
$lang = $request->getLocale(); $lang = $request->getLocale();
try { try {

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\WopiBundle\Tests\Controller; namespace Chill\WopiBundle\Tests\Controller;
use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\WopiBundle\Controller\ConvertController; use Chill\WopiBundle\Controller\ConvertController;
use Chill\WopiBundle\Service\WopiConverter; use Chill\WopiBundle\Service\WopiConverter;
@ -37,6 +38,7 @@ final class ConvertControllerTest extends TestCase
$security = $this->prophesize(Security::class); $security = $this->prophesize(Security::class);
$security->isGranted('ROLE_USER')->willReturn(true); $security->isGranted('ROLE_USER')->willReturn(true);
$security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)->willReturn(true);
$storeManager = $this->prophesize(StoredObjectManagerInterface::class); $storeManager = $this->prophesize(StoredObjectManagerInterface::class);
$storeManager->read($storedObject)->willReturn('content'); $storeManager->read($storedObject)->willReturn('content');
@ -67,6 +69,7 @@ final class ConvertControllerTest extends TestCase
$security = $this->prophesize(Security::class); $security = $this->prophesize(Security::class);
$security->isGranted('ROLE_USER')->willReturn(true); $security->isGranted('ROLE_USER')->willReturn(true);
$security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)->willReturn(true);
$storeManager = $this->prophesize(StoredObjectManagerInterface::class); $storeManager = $this->prophesize(StoredObjectManagerInterface::class);
$storeManager->read($storedObject)->willReturn('content'); $storeManager->read($storedObject)->willReturn('content');