diff --git a/CHANGELOG.md b/CHANGELOG.md
index 48e2ad7dd..ccfbf6bb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,10 @@ and this project adheres to
## Unreleased
+* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
+* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
+* [person] Add title to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
+
## Test releases
@@ -33,7 +37,7 @@ and this project adheres to
* [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434)
* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435)
* [parcours]: Order social activities and only display most recent three in parcours resumé (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/481)
-* [3party]: 3party: redirect to parent when contact (child) is opened in view page
+* [3party]: 3party: redirect to parent when contact (child) is opened in view page
* [parcours / addresses]: launch an event when a person change address (either through changing household or because the household is associated to a new address). If the person is localising a course, the course location go back to a temporarily address.
* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType
diff --git a/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue b/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue
index cd5121fc9..2ab033f2e 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue
+++ b/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue
@@ -16,8 +16,8 @@
{{ $t('choose_a_template') }}
-
- {{ t.name.fr || 'Aucun nom défini' }}
+
+ {{ t.name.fr || 'Aucun nom défini' }}
diff --git a/src/Bundle/ChillDocStoreBundle/DependencyInjection/ChillDocStoreExtension.php b/src/Bundle/ChillDocStoreBundle/DependencyInjection/ChillDocStoreExtension.php
index 036a50722..957624702 100644
--- a/src/Bundle/ChillDocStoreBundle/DependencyInjection/ChillDocStoreExtension.php
+++ b/src/Bundle/ChillDocStoreBundle/DependencyInjection/ChillDocStoreExtension.php
@@ -17,6 +17,7 @@ use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
@@ -46,6 +47,28 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
$this->prependRoute($container);
$this->prependAuthorization($container);
$this->prependTwig($container);
+ $this->prependApis($container);
+ }
+
+ protected function prependApis(ContainerBuilder $container)
+ {
+ $container->prependExtensionConfig('chill_main', [
+ 'apis' => [
+ [
+ 'class' => \Chill\DocStoreBundle\Entity\StoredObject::class,
+ 'name' => 'stored_object',
+ 'base_path' => '/api/1.0/docstore/stored-object',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]);
}
protected function prependAuthorization(ContainerBuilder $container)
diff --git a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
index 251a7513d..c512dba73 100644
--- a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
+++ b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
@@ -34,19 +34,19 @@ class StoredObject implements AsyncFileInterface, Document
{
/**
* @ORM\Column(type="datetime", name="creation_date")
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
*/
private DateTimeInterface $creationDate;
/**
* @ORM\Column(type="json", name="datas")
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
*/
private array $datas = [];
/**
* @ORM\Column(type="text")
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
*/
private $filename;
@@ -54,30 +54,32 @@ class StoredObject implements AsyncFileInterface, Document
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
*/
private $id;
/**
* @var int[]
* @ORM\Column(type="json", name="iv")
+ * @Serializer\Groups({"write"})
*/
private array $iv = [];
/**
* @ORM\Column(type="json", name="key")
+ * @Serializer\Groups({"write"})
*/
private array $keyInfos = [];
/**
* @ORM\Column(type="text", name="type")
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
*/
private string $type = '';
/**
* @ORM\Column(type="uuid", unique=true)
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
*/
private UuidInterface $uuid;
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js b/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js
index 6cb68fcea..35871245b 100644
--- a/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js
+++ b/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js
@@ -4,12 +4,12 @@ var initializeDownload = require('./downloader.js');
/**
- *
+ *
* define a dropzone for chill usage
- *
- * An event is launched when dropzone is initialize, allowing to customize events
+ *
+ * An event is launched when dropzone is initialize, allowing to customize events
* on dropzone :
- *
+ *
* ```
* window.addEventListener("chill_dropzone_initialized", (e) => {
* // do something with dropzone:
@@ -18,7 +18,7 @@ var initializeDownload = require('./downloader.js');
* });
* });
* ```
- *
+ *
*/
// load css
@@ -37,7 +37,6 @@ var keyDefinition = {
var searchForZones = function(root) {
var zones = root.querySelectorAll('div[data-stored-object]');
-
for(let i=0; i < zones.length; i++) {
initialize(zones[i]);
}
@@ -48,32 +47,32 @@ var getUploadUrl = function(zoneData, files) {
generateTempUrlPost = zoneData.zone.querySelector('input[data-async-file-upload]').dataset.generateTempUrlPost,
oReq = new XMLHttpRequest()
;
-
+
// arg, dropzone, you cannot handle async upload...
oReq.open("GET", generateTempUrlPost, false);
oReq.send();
-
+
if (oReq.readyState !== XMLHttpRequest.DONE) {
throw new Error("Error while fetching url to upload");
}
-
+
zoneData.params = JSON.parse(oReq.responseText);
-
+
return zoneData.params.url;
};
var encryptFile = function(originalFile, zoneData, done) {
- var
+ var
iv = crypto.getRandomValues(new Uint8Array(16)),
reader = new FileReader(),
jsKey, rawKey
;
-
+
zoneData.originalType = originalFile.type;
-
+
reader.onload = e => {
window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ])
- .then(key => {
+ .then(key => {
jsKey = key;
// we register the key somwhere
@@ -90,34 +89,34 @@ var encryptFile = function(originalFile, zoneData, done) {
rawKey: rawKey,
iv: iv
};
-
+
done(new File( [ encrypted ], zoneData.suffix));
});
};
-
+
reader.readAsArrayBuffer(originalFile);
};
var addBelowButton = (btn, zone, zoneData) => {
let
belowZone = zone.querySelector('.chill-dropzone__below-zone');
-
+
if (belowZone === null) {
belowZone = document.createElement('div');
belowZone.classList.add('chill-dropzone__below-zone');
zone.appendChild(belowZone);
}
-
+
belowZone.appendChild(btn);
};
var createZone = (zone, zoneData) => {
- var
+ var
created = document.createElement('div'),
initMessage = document.createElement('div'),
initContent = zone.dataset.labelInitMessage,
dropzoneI;
-
+
created.classList.add('dropzone');
initMessage.classList.add('dz-message');
initMessage.appendChild(document.createTextNode(initContent));
@@ -142,7 +141,7 @@ var createZone = (zone, zoneData) => {
return zoneData.suffix;
}
});
-
+
dropzoneI.on("sending", function(file, xhr, formData) {
formData.append("redirect", zoneData.params.redirect);
formData.append("max_file_size", zoneData.params.max_file_size);
@@ -150,24 +149,24 @@ var createZone = (zone, zoneData) => {
formData.append("expires", zoneData.params.expires);
formData.append("signature", zoneData.params.signature);
});
-
+
dropzoneI.on("success", function(file, response) {
zoneData.currentFile = file;
storeDataInForm(zone, zoneData);
});
-
+
dropzoneI.on("addedfile", function(file) {
if (zoneData.hasOwnProperty('currentFile')) {
dropzoneI.removeFile(zoneData.currentFile);
}
});
-
+
dropzoneI.on("removedfile", function(file) {
removeDataInForm(zone, zoneData);
});
zone.insertBefore(created, zone.firstChild);
-
+
let event = new CustomEvent("chill_dropzone_initialized", {
detail: {
dropzone: dropzoneI,
@@ -179,7 +178,7 @@ var createZone = (zone, zoneData) => {
var initialize = function(zone) {
- var
+ var
allowRemove = zone.dataset.allowRemove,
zoneData = { zone: zone, suffix: createFilename(), allowRemove: allowRemove, old: null }
;
@@ -204,13 +203,13 @@ var createFilename = () => {
};
var storeDataInForm = (zone, zoneData) => {
- var
+ var
inputKey = zone.querySelector('input[data-stored-object-key]'),
inputIv = zone.querySelector('input[data-stored-object-iv]'),
inputObject = zone.querySelector('input[data-async-file-upload]'),
inputType = zone.querySelector('input[data-async-file-type]')
;
-
+
inputKey.value = JSON.stringify(zoneData.crypto.rawKey);
inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv));
inputType.value = zoneData.originalType;
@@ -220,18 +219,18 @@ var storeDataInForm = (zone, zoneData) => {
};
const restoreDataInForm = (zone, zoneData) => {
- var
+ var
inputKey = zone.querySelector('input[data-stored-object-key]'),
inputIv = zone.querySelector('input[data-stored-object-iv]'),
inputObject = zone.querySelector('input[data-async-file-upload]'),
inputType = zone.querySelector('input[data-async-file-type]')
;
-
+
if (zoneData.old === null) {
console.log('should not have restored data');
return;
}
-
+
inputKey.value = zoneData.old.key;
inputIv.value = zoneData.old.iv;
inputType.value = zoneData.old.type;
@@ -241,21 +240,21 @@ const restoreDataInForm = (zone, zoneData) => {
};
const hasDataInForm = (zone, zoneData) => {
- var
+ var
inputObject = zone.querySelector('input[data-async-file-upload]')
;
-
+
return inputObject.value.length > 0;
};
var removeDataInForm = (zone, zoneData) => {
- var
+ var
inputKey = zone.querySelector('input[data-stored-object-key]'),
inputIv = zone.querySelector('input[data-stored-object-iv]'),
inputObject = zone.querySelector('input[data-async-file-upload]'),
inputType = zone.querySelector('input[data-async-file-type]')
;
-
+
// store data for future usage
zoneData.old = {
key: inputKey.value,
@@ -268,7 +267,7 @@ var removeDataInForm = (zone, zoneData) => {
inputIv.value = "";
inputType.value = "";
inputObject.value = "";
-
+
insertDownloadButton(zone);
};
@@ -279,25 +278,25 @@ var insertRemoveButton = (zone, zoneData) => {
labelRemove = zone.dataset.dictRemove,
labelCancel = 'Restaurer'
;
-
+
removeButton.classList.add('btn', 'btn-delete');
removeButton.textContent = labelRemove;
-
+
cancelButton.classList.add('btn', 'btn-cancel');
cancelButton.textContent = labelCancel;
-
+
removeButton.addEventListener('click', (e) => {
e.preventDefault();
if (zoneData.allowRemove === 'true') {
removeDataInForm(zone, zoneData);
cancelButton.addEventListener('click', (e) => {
e.preventDefault();
-
+
restoreDataInForm(zone, zoneData);
-
+
cancelButton.remove();
zone.querySelector('.dropzone').remove();
-
+
initialize(zone);
});
}
@@ -306,16 +305,16 @@ var insertRemoveButton = (zone, zoneData) => {
removeButton.remove();
createZone(zone, zoneData);
});
-
+
addBelowButton(removeButton, zone, zoneData);
// zone.appendChild(removeButton);
};
const removeDownloadButton = (zone, zoneData) => {
- var
+ var
existingButtons = zone.querySelectorAll('a[data-download-button]')
;
-
+
// remove existing
existingButtons.forEach(function(b) {
b.remove();
@@ -323,7 +322,7 @@ const removeDownloadButton = (zone, zoneData) => {
};
var insertDownloadButton = (zone, zoneData) => {
- var
+ var
existingButtons = zone.querySelectorAll('a[data-download-button]'),
newButton = document.createElement('a'),
inputKey = zone.querySelector('input[data-stored-object-key]'),
@@ -336,18 +335,18 @@ var insertDownloadButton = (zone, zoneData) => {
tempUrlGenerator = zone.dataset.tempUrlGenerator,
tempUrlGeneratorParams = new URLSearchParams()
;
-
+
// remove existing
existingButtons.forEach(function(b) {
b.remove();
});
-
+
if (inputObject.value === '') {
return;
}
-
+
tempUrlGeneratorParams.append('object_name', inputObject.value);
-
+
newButton.dataset.downloadButton = true;
newButton.dataset.key = inputKey.value;
newButton.dataset.iv = inputIv.value;
@@ -357,7 +356,7 @@ var insertDownloadButton = (zone, zoneData) => {
newButton.dataset.tempUrlGetGenerator = tempUrlGenerator + '?' + tempUrlGeneratorParams.toString();
newButton.classList.add('btn', 'btn-download', 'dz-bt-below-dropzone');
newButton.textContent = labelQuietButton;
-
+
addBelowButton(newButton, zone, zoneData);
//zone.appendChild(newButton);
initializeDownload(zone);
@@ -370,3 +369,5 @@ window.addEventListener('load', function(e) {
window.addEventListener('collection-add-entry', function(e) {
searchForZones(e.detail.entry);
});
+
+export { searchForZones };
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/_components/AddAsyncUpload.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/_components/AddAsyncUpload.vue
new file mode 100644
index 000000000..e931f1b68
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/_components/AddAsyncUpload.vue
@@ -0,0 +1,156 @@
+
+
+ {{ $t(buttonTitle) }}
+
+
+
+
+
+
+ {{ $t('upload_a_document') }}
+
+
+
+
+
+
+
+
+ {{ $t('action.add')}}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectDenormalizer.php b/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectDenormalizer.php
new file mode 100644
index 000000000..762aed7fb
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectDenormalizer.php
@@ -0,0 +1,51 @@
+storedObjectRepository = $storedObjectRepository;
+ }
+
+ public function denormalize($data, $type, $format = null, array $context = [])
+ {
+ if (array_key_exists(AbstractNormalizer::OBJECT_TO_POPULATE, $context)) {
+ return $context[AbstractNormalizer::OBJECT_TO_POPULATE];
+ }
+
+ return $this->storedObjectRepository->find($data['id']);
+ }
+
+ public function supportsDenormalization($data, $type, $format = null)
+ {
+ if (false === is_array($data)) {
+ return false;
+ }
+
+ if (false === array_key_exists('id', $data)) {
+ return false;
+ }
+
+ return StoredObject::class === $type;
+ }
+}
diff --git a/src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml b/src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml
new file mode 100644
index 000000000..d74311387
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml
@@ -0,0 +1,46 @@
+---
+openapi: "3.0.0"
+info:
+ version: "1.0.0"
+ title: "Chill api"
+ description: "Api documentation for chill. Currently, work in progress"
+servers:
+ - url: "/api"
+ description: "Your current dev server"
+
+components:
+ schemas:
+ StoredObject:
+ type: object
+ properties:
+ id:
+ type: integer
+ filename:
+ type: string
+ type:
+ type: string
+
+paths:
+ /1.0/docstore/stored-object.json:
+ post:
+ tags:
+ - storedobject
+ summary: Create a stored object
+ requestBody:
+ description: "A stored object"
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/StoredObject"
+ responses:
+ 200:
+ description: "OK"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/StoredObject"
+ 403:
+ description: "Unauthorized"
+ 422:
+ description: "Invalid data"
diff --git a/src/Bundle/ChillDocStoreBundle/chill.webpack.config.js b/src/Bundle/ChillDocStoreBundle/chill.webpack.config.js
index 833052ef4..c9b2b8877 100644
--- a/src/Bundle/ChillDocStoreBundle/chill.webpack.config.js
+++ b/src/Bundle/ChillDocStoreBundle/chill.webpack.config.js
@@ -1,4 +1,7 @@
module.exports = function(encore)
{
+ encore.addAliases({
+ ChillDocStoreAssets: __dirname + '/Resources/public'
+ });
encore.addEntry('mod_async_upload', __dirname + '/Resources/public/module/async_upload/index.js');
};
diff --git a/src/Bundle/ChillDocStoreBundle/config/services.yaml b/src/Bundle/ChillDocStoreBundle/config/services.yaml
index 5c73c3479..ff2818f6b 100644
--- a/src/Bundle/ChillDocStoreBundle/config/services.yaml
+++ b/src/Bundle/ChillDocStoreBundle/config/services.yaml
@@ -33,3 +33,10 @@ services:
resource: './../Workflow/'
autoconfigure: true
autowire: true
+
+ Chill\DocStoreBundle\Serializer\Normalizer\:
+ autowire: true
+ resource: '../Serializer/Normalizer/'
+ tags:
+ - { name: 'serializer.normalizer', priority: 16 }
+
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php
index 68f508b95..247c7aace 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php
@@ -70,9 +70,10 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
* @ORM\OneToMany(
* targetEntity=AccompanyingPeriodWorkEvaluationDocument::class,
* mappedBy="accompanyingPeriodWorkEvaluation",
- * cascade={"remove"}
+ * cascade={"remove", "persist"}
* )
- * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"read", "write"})
+ * @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private Collection $documents;
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php
index cbb4231b8..e4a15fccc 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php
@@ -61,7 +61,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* @ORM\SequenceGenerator(sequenceName="chill_person_social_work_eval_doc_id_seq", allocationSize=1, initialValue=1000)
* @Serializer\Groups({"read"})
*/
- private ?int $id;
+ private ?int $id = null;
/**
* @ORM\ManyToOne(
@@ -69,6 +69,8 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* cascade={"remove"},
* )
* @Serializer\Groups({"read"})
+ * @Serializer\Groups({"write"})
+ * @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?StoredObject $storedObject = null;
@@ -80,6 +82,14 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
*/
private ?DocGeneratorTemplate $template = null;
+ /**
+ * @ORM\Column(type="text", nullable=true)
+ * @Serializer\Groups({"read"})
+ * @Serializer\Groups({"write"})
+ * @Serializer\Groups({"accompanying_period_work_evaluation:create"})
+ */
+ private ?string $title = null;
+
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
@@ -127,6 +137,11 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this->template;
}
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
/**
* @return DateTimeImmutable|null
*/
@@ -185,6 +200,13 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this;
}
+ public function setTitle(?string $title): AccompanyingPeriodWorkEvaluationDocument
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface
{
$this->updatedAt = $datetime;
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
index cabebf973..49e53dbc3 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
@@ -65,14 +65,31 @@
{{ $t('Documents') }} :
-
+
-
{{ d.template.name.fr }}
-
-
Créé par {{ d.createdBy.text }}
- Le {{ $d(ISOToDatetime(d.createdAt.datetime), 'long') }}
-
-
+
+
+ {{ $t('document_title') }}
+
+
+
+
+
+
+ {{ $t('template_title') }}
+
+
{{ d.template.name.fr }}
+
+
+
+
Créé par {{ d.createdBy.text }}
+ Le {{ $d(ISOToDatetime(d.createdAt.datetime), 'long') }}
+
@@ -81,6 +98,10 @@
+
+
+
+
@@ -88,6 +109,7 @@
+
{{ $t('document_add') }} :
{{ $t('evaluation_generate_a_document') }}
+
+
{{ $t('document_upload') }}
+
+
+
@@ -111,6 +147,7 @@ import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
import { mapGetters, mapState } from 'vuex';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
+import AddAsyncUpload from 'ChillDocStoreAssets/vuejs/_components/AddAsyncUpload.vue';
const i18n = {
messages: {
@@ -129,6 +166,11 @@ const i18n = {
evaluation_add_a_document: "Ajouter un document",
evaluation_add: "Ajouter une évaluation",
Documents: "Documents",
+ document_add: "Générer ou téléverser un document",
+ document_upload: "Téléverser un document",
+ document_title: "Titre du document",
+ template_title: "Nom du template",
+ browse: "Ajouter un document"
}
}
};
@@ -139,14 +181,23 @@ export default {
components: {
ckeditor: CKEditor.component,
PickTemplate,
+ AddAsyncUpload
},
i18n,
data() {
return {
editor: ClassicEditor,
template: null,
+ asyncUploadOptions: {
+ maxFiles: 1,
+ maxPostSize: 15000000,
+ required: false,
+ }
}
},
+ mounted() {
+ console.log(this.evaluation)
+ },
computed: {
...mapState([
'isPosting'
@@ -162,7 +213,6 @@ export default {
return dateToISO(this.evaluation.startDate);
},
set(v) {
- console.log(v);
this.$store.commit('setEvaluationStartDate', { key: this.evaluation.key, date: ISOToDate(v) });
}
},
@@ -205,11 +255,11 @@ export default {
})
;
},
- buildEditLink(storedObject) {
- return `/wopi/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent(
+ buildEditLink(storedObject) {
+ return `/wopi/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent(
window.location.pathname + window.location.search + window.location.hash);
- },
- submitBeforeGenerate({template}) {
+ },
+ submitBeforeGenerate({template}) {
const callback = (data) => {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
@@ -217,6 +267,21 @@ export default {
};
return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; });
+ },
+ onInputDocumentTitle(event) {
+ const id = Number(event.target.id);
+ const title = event.target.value;
+ this.$store.commit('updateDocumentTitle', {id: id, evaluationKey: this.evaluation.key, title: title});
+ },
+ addDocument(storedObject) {
+ let document = {
+ type: 'accompanying_period_work_evaluation_document',
+ storedObject: storedObject
+ };
+ this.$store.commit('addDocument', {key: this.evaluation.key, document: document});
+ },
+ removeDocument(document) {
+ this.$store.commit('removeDocument', {key: this.evaluation.key, document: document});
}
},
}
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js
index b5421ea48..89c6c8811 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js
@@ -110,6 +110,7 @@ const store = createStore({
maxDate: e.maxDate !== null ? { datetime: datetimeToISO(e.maxDate) } : null,
warningInterval: intervalDaysToISO(e.warningInterval),
comment: e.comment,
+ documents: e.documents
};
if (e.id !== undefined) {
o.id = e.id;
@@ -197,6 +198,18 @@ const store = createStore({
found.results = found.results.filter(r => r.id !== result.id);
},
+ addDocument(state, payload) {
+ state.evaluationsPicked.find(e => e.key === payload.key).documents.push(payload.document);
+ },
+ removeDocument(state, payload) {
+ let evaluations = state.evaluationsPicked.find(e => e.key === payload.key);
+
+ if (evaluations === undefined) {
+ return;
+ }
+
+ evaluations.documents = evaluations.documents.filter(d => d.id !== payload.document.id);
+ },
addEvaluation(state, evaluation) {
let e = {
type: "accompanying_period_work_evaluation",
@@ -284,6 +297,10 @@ const store = createStore({
setIsPosting(state, st) {
state.isPosting = st;
},
+ updateDocumentTitle(state, payload) {
+ state.evaluationsPicked.find(e => e.key === payload.evaluationKey)
+ .documents.find(d => d.id === payload.id).title = payload.title;
+ }
},
actions: {
updateThirdParty({ commit }, payload) {
@@ -374,13 +391,18 @@ const store = createStore({
});
}
},
+ addDocument({commit}, payload) {
+ commit('addDocument', payload);
+ },
+ removeDocument({commit}, payload) {
+ commit('removeDocument', payload);
+ },
submit({ getters, state, commit }, callback) {
let
payload = getters.buildPayload,
url = `/api/1.0/person/accompanying-course/work/${state.work.id}.json`,
errors = []
;
-
commit('setIsPosting', true);
return makeFetch('PUT', url, payload)
@@ -397,6 +419,9 @@ const store = createStore({
commit('setErrors', error.violations);
});
},
+ updateDocumentTitle({commit}, payload) {
+ commit('updateDocumentTitle', payload)
+ }
}
});
diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkEvaluationDenormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkEvaluationDenormalizer.php
new file mode 100644
index 000000000..12bd254c5
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkEvaluationDenormalizer.php
@@ -0,0 +1,137 @@
+workRepository = $workRepository;
+ $this->em = $em;
+ }
+
+ public function denormalize($data, $type, $format = null, array $context = [])
+ {
+ $evaluation = $this->denormalizer->denormalize($data, $type, $format, array_merge(
+ $context,
+ ['skip' => self::class]
+ ));
+
+ //if (in_array('accompanying_period_work:edit', $context['groups'] ?? [], true)) {
+ $this->handleEvaluationCollection($data, $evaluation, $format, $context);
+ //}
+
+ return $evaluation;
+ }
+
+ public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
+ {
+ return AccompanyingPeriodWorkEvaluation::class === $type
+ && self::class !== ($context['skip'] ?? null)
+ && is_array($data)
+ && array_key_exists('type', $data)
+ && 'accompanying_period_work_evaluation' === $data['type'];
+ }
+
+ private function handleEvaluationCollection(array $data, AccompanyingPeriodWorkEvaluation $evaluation, string $format, array $context)
+ {
+ $dataById = [];
+ $dataWithoutId = [];
+
+ foreach ($data['documents'] as $e) {
+ if (array_key_exists('id', $e)) {
+ $dataById[$e['id']] = $e;
+ } else {
+ $dataWithoutId[] = $e;
+ }
+ }
+ dump($dataById);
+ dump($dataWithoutId);
+
+ dump($evaluation);
+ //partition the separate kept documents and removed one
+ [$kept, $removed] = $evaluation->getDocuments()
+ ->partition(
+ static fn (int $key, AccompanyingPeriodWorkEvaluationDocument $a) => array_key_exists($a->getId(), $dataById)
+ );
+ //$kept = $evaluation->getDocuments();
+ dump($kept);
+ dump($removed);
+ // remove the document from evaluation
+ foreach ($removed as $r) {
+ dump($r);
+ $evaluation->removeDocument($r);
+ }
+ // handle the documents kept
+ foreach ($kept as $k) {
+ dump($k); // Cannot iterate over $kept which is a PersistentCollection
+ $evaluation->removeDocument($k);
+ dump($evaluation);
+ $document = $this->denormalizer->denormalize(
+ $dataById[$k->getId()],
+ AccompanyingPeriodWorkEvaluationDocument::class,
+ $format,
+ array_merge(
+ $context,
+ [
+ 'groups' => ['write'],
+ AbstractNormalizer::OBJECT_TO_POPULATE => $k,
+ ]
+ )
+ );
+
+ $evaluation->addDocument($document);
+ }
+ // create new document
+ foreach ($dataWithoutId as $newData) {
+ dump($newData);
+ $document = $this->denormalizer->denormalize(
+ $newData,
+ AccompanyingPeriodWorkEvaluationDocument::class,
+ $format,
+ array_merge(
+ $context,
+ ['groups' => ['accompanying_period_work_evaluation:create']]
+ )
+ );
+ $evaluation->addDocument($document);
+ }
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20220224145951.php b/src/Bundle/ChillPersonBundle/migrations/Version20220224145951.php
new file mode 100644
index 000000000..e71174eea
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/migrations/Version20220224145951.php
@@ -0,0 +1,36 @@
+addSql('ALTER TABLE chill_person_accompanying_period_work_evaluation_document DROP title');
+ }
+
+ public function getDescription(): string
+ {
+ return 'Add title to AccompanyingPeriodWorkEvaluationDocument';
+ }
+
+ public function up(Schema $schema): void
+ {
+ $this->addSql('ALTER TABLE chill_person_accompanying_period_work_evaluation_document ADD title TEXT DEFAULT NULL');
+ }
+}