mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
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:
commit
f04ef3c3e3
@ -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'],
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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];
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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');
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
@ -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');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user