mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-12 13:24:25 +00:00
upload and retrieve document
This commit is contained in:
parent
eda8f2c033
commit
b5bf8b8884
@ -26,12 +26,15 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
|
||||
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('services.yml');
|
||||
$loader->load('services/media.yml');
|
||||
|
||||
}
|
||||
|
||||
public function prepend(ContainerBuilder $container)
|
||||
{
|
||||
$this->prependRoute($container);
|
||||
$this->prependAuthorization($container);
|
||||
$this->prependTwig($container);
|
||||
}
|
||||
|
||||
protected function prependRoute(ContainerBuilder $container)
|
||||
@ -40,7 +43,8 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
|
||||
$container->prependExtensionConfig('chill_main', array(
|
||||
'routing' => array(
|
||||
'resources' => array(
|
||||
'@ChillDocStoreBundle/Resources/config/routing.yml'
|
||||
'@ChillDocStoreBundle/Resources/config/routing.yml',
|
||||
'@ChampsLibresAsyncUploaderBundle/Resources/config/routing.yml'
|
||||
)
|
||||
)
|
||||
));
|
||||
@ -57,4 +61,12 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
protected function prependTwig(ContainerBuilder $container)
|
||||
{
|
||||
$twigConfig = array(
|
||||
'form_themes' => array('@ChillDocStore/Form/fields.html.twig')
|
||||
);
|
||||
$container->prependExtensionConfig('twig', $twigConfig);
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +40,12 @@ class Document implements HasScopeInterface
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="Chill\DocStoreBundle\Entity\StoredObject",
|
||||
* cascade={"persist"}
|
||||
* )
|
||||
*/
|
||||
private $content;
|
||||
private $object;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
|
||||
@ -65,7 +68,7 @@ class Document implements HasScopeInterface
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
@ -157,4 +160,16 @@ class Document implements HasScopeInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObject(): ?StoredObject
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
public function setObject(StoredObject $object)
|
||||
{
|
||||
$this->object = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
164
Entity/StoredObject.php
Normal file
164
Entity/StoredObject.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
||||
|
||||
/**
|
||||
* Represent a document stored in an object store
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table("chill_doc.stored_object")
|
||||
*/
|
||||
class StoredObject implements AsyncFileInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id()
|
||||
* @ORM\GeneratedValue()
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
*/
|
||||
private $filename;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json_array", name="key")
|
||||
* @var array
|
||||
*/
|
||||
private $keyInfos = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int[]
|
||||
* @ORM\Column(type="json_array", name="iv")
|
||||
*/
|
||||
private $iv = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \DateTime
|
||||
* @ORM\Column(type="datetime", name="creation_date")
|
||||
*/
|
||||
private $creationDate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
* @ORM\Column(type="text", name="type")
|
||||
*/
|
||||
private $type = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
* @ORM\Column(type="json_array", name="datas")
|
||||
*/
|
||||
private $datas = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->creationDate = new \DateTime();
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function getCreationDate(): \DateTime
|
||||
{
|
||||
return $this->creationDate;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getDatas()
|
||||
{
|
||||
return $this->datas;
|
||||
}
|
||||
|
||||
public function setFilename($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCreationDate(\DateTime $creationDate)
|
||||
{
|
||||
$this->creationDate = $creationDate;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDatas(array $datas)
|
||||
{
|
||||
$this->datas = $datas;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObjectName()
|
||||
{
|
||||
return $this->getFilename();
|
||||
}
|
||||
|
||||
public function setAsyncFile(/*AsyncFileInterface*/ $async)
|
||||
{
|
||||
dump($async);
|
||||
//$this->setFilename($async->getObjectName());
|
||||
}
|
||||
|
||||
public function getAsyncFile()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKeyInfos()
|
||||
{
|
||||
return $this->keyInfos;
|
||||
}
|
||||
|
||||
public function getIv()
|
||||
{
|
||||
return $this->iv;
|
||||
}
|
||||
|
||||
public function setKeyInfos($keyInfos)
|
||||
{
|
||||
$this->keyInfos = $keyInfos;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setIv($iv)
|
||||
{
|
||||
$this->iv = $iv;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -65,7 +65,7 @@ class PersonDocumentType extends AbstractType
|
||||
->add('description', TextareaType::class, [
|
||||
'required' => false
|
||||
])
|
||||
->add('content')
|
||||
->add('object', StoredObjectType::class)
|
||||
->add('scope', ScopePickerType::class, [
|
||||
'center' => $options['center'],
|
||||
'role' => $options['role']
|
||||
|
72
Form/StoredObjectType.php
Normal file
72
Form/StoredObjectType.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/*
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use ChampsLibres\AsyncUploaderBundle\Form\Type\AsyncUploaderType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class StoredObjectType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('filename', AsyncUploaderType::class)
|
||||
->add('keyInfos', HiddenType::class)
|
||||
->add('iv', HiddenType::class)
|
||||
;
|
||||
|
||||
$builder
|
||||
->get('keyInfos')
|
||||
->addModelTransformer(new CallbackTransformer(
|
||||
[$this, 'transform'], [$this, 'reverseTransform']
|
||||
))
|
||||
;
|
||||
$builder
|
||||
->get('iv')
|
||||
->addModelTransformer(new CallbackTransformer(
|
||||
[$this, 'transform'], [$this, 'reverseTransform']
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'stored_object';
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('data_class', StoredObject::class)
|
||||
;
|
||||
}
|
||||
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return \json_decode($value, true);
|
||||
}
|
||||
|
||||
public function transform($object)
|
||||
{
|
||||
if ($object === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return \json_encode($object);
|
||||
}
|
||||
}
|
53
Object/ObjectToAsyncFileTransformer.php
Normal file
53
Object/ObjectToAsyncFileTransformer.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\Object;
|
||||
|
||||
use ChampsLibres\AsyncUploaderBundle\Form\AsyncFileTransformer\AsyncFileTransformerInterface;
|
||||
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class ObjectToAsyncFileTransformer implements AsyncFileTransformerInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function toAsyncFile($data)
|
||||
{
|
||||
dump($data);
|
||||
|
||||
if ($data instanceof StoredObject) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function toData(AsyncFileInterface $asyncFile)
|
||||
{
|
||||
dump($asyncFile);
|
||||
|
||||
$object = $this->em
|
||||
->getRepository(StoredObject::class)
|
||||
->findByFilename($asyncFile->getObjectName())
|
||||
;
|
||||
|
||||
return $object ?? (new StoredObject())
|
||||
->setFilename($asyncFile->getObjectName())
|
||||
;
|
||||
}
|
||||
}
|
40
Object/PersistenceChecker.php
Normal file
40
Object/PersistenceChecker.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\Object;
|
||||
|
||||
use ChampsLibres\AsyncUploaderBundle\Persistence\PersistenceCheckerInterface;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class PersistenceChecker implements PersistenceCheckerInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
|
||||
public function isPersisted($object_name): bool
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$qb->select('COUNT(m)')
|
||||
->from(StoredObject::class, 'm')
|
||||
->where($qb->expr()->eq('m.filename', ':object_name'))
|
||||
->setParameter('object_name', $object_name)
|
||||
;
|
||||
|
||||
return 1 === $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
}
|
9
Resources/config/services/media.yml
Normal file
9
Resources/config/services/media.yml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
chill_doc_store.persistence_checker:
|
||||
class: Chill\DocStoreBundle\Object\PersistenceChecker
|
||||
arguments:
|
||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||
|
||||
Chill\DocStoreBundle\Object\ObjectToAsyncFileTransformer:
|
||||
arguments:
|
||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
38
Resources/migrations/Version20180606133338.php
Normal file
38
Resources/migrations/Version20180606133338.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Add schema for stored object
|
||||
*/
|
||||
final class Version20180606133338 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema) : void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('CREATE SEQUENCE chill_doc.stored_object_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_doc.stored_object (id INT NOT NULL, filename TEXT NOT NULL, key JSON NOT NULL, iv JSON NOT NULL, creation_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, type TEXT NOT NULL, datas JSON NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('COMMENT ON COLUMN chill_doc.stored_object.key IS \'(DC2Type:json_array)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_doc.stored_object.iv IS \'(DC2Type:json_array)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_doc.stored_object.datas IS \'(DC2Type:json_array)\'');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD object_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document DROP content');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C232D562B FOREIGN KEY (object_id) REFERENCES chill_doc.stored_object (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_41DA53C232D562B ON chill_doc.person_document (object_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document DROP CONSTRAINT FK_41DA53C232D562B');
|
||||
$this->addSql('DROP SEQUENCE chill_doc.stored_object_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_doc.stored_object');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD content TEXT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document DROP object_id');
|
||||
}
|
||||
}
|
67
Resources/public/module/async_upload/downloader.js
Normal file
67
Resources/public/module/async_upload/downloader.js
Normal file
@ -0,0 +1,67 @@
|
||||
var algo = 'AES-CBC';
|
||||
|
||||
var initializeButtons = (root) => {
|
||||
var
|
||||
buttons = root.querySelectorAll('a[data-download-button]');
|
||||
|
||||
for (let i = 0; i < buttons.length; i ++) {
|
||||
initialize(buttons[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var initialize = (button) => {
|
||||
button.addEventListener('click', onClick);
|
||||
};
|
||||
|
||||
var onClick = e => download(e.target);
|
||||
|
||||
var download = (button) => {
|
||||
console.log(button.dataset.key);
|
||||
var
|
||||
keyData = JSON.parse(button.dataset.key),
|
||||
//keyData, // = JSON.parse(keyString),
|
||||
ivData = JSON.parse(button.dataset.iv),
|
||||
iv = new Uint8Array(ivData),
|
||||
url = button.dataset.tempUrlGet,
|
||||
data, key
|
||||
;
|
||||
console.log('keyData', keyData);
|
||||
console.log('iv', iv);
|
||||
console.log('url', url);
|
||||
|
||||
window.crypto.subtle.importKey('jwk', keyData, { name: algo, iv: iv}, false, ['decrypt'])
|
||||
.then(nKey => {
|
||||
key = nKey;
|
||||
console.log(key);
|
||||
|
||||
return window.fetch(url);
|
||||
})
|
||||
.then(r => {
|
||||
if (r.ok) {
|
||||
return r.arrayBuffer();
|
||||
} else {
|
||||
console.log(r);
|
||||
throw new Error(r.statusCode);
|
||||
}
|
||||
})
|
||||
.then(buffer => {
|
||||
return window.crypto.subtle.decrypt({ name: algo, iv: iv }, key, buffer);
|
||||
})
|
||||
.then(decrypted => {
|
||||
var
|
||||
blob = new Blob([decrypted]),
|
||||
url = window.URL.createObjectURL(blob)
|
||||
;
|
||||
button.href = url;
|
||||
button.removeEventListener('click', onClick);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
window.addEventListener('load', function(e) {
|
||||
console.log('load');
|
||||
initializeButtons(e.target);
|
||||
});
|
2
Resources/public/module/async_upload/index.js
Normal file
2
Resources/public/module/async_upload/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
require('./uploader.js');
|
||||
require('./downloader.js');
|
183
Resources/public/module/async_upload/uploader.js
Normal file
183
Resources/public/module/async_upload/uploader.js
Normal file
@ -0,0 +1,183 @@
|
||||
var algo = 'AES-CBC';
|
||||
|
||||
var keyDefinition = {
|
||||
name: algo,
|
||||
length: 256
|
||||
};
|
||||
|
||||
var searchForZones = function(root) {
|
||||
var zones = root.querySelectorAll('div[data-stored-object]');
|
||||
|
||||
for(let i=0; i < zones.length; i++) {
|
||||
initialize(zones[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var initialize = function(zone) {
|
||||
console.log('initialize zone');
|
||||
|
||||
var
|
||||
dropZone = document.createElement('div'),
|
||||
input = document.createElement('input')
|
||||
;
|
||||
|
||||
input.type = 'file';
|
||||
input.addEventListener('change', function(e) {
|
||||
handleInputFile(zone);
|
||||
});
|
||||
|
||||
dropZone.classList.add('chill-doc__dropzone__drop');
|
||||
|
||||
zone.insertBefore(input, zone.firstChild);
|
||||
zone.insertBefore(dropZone, zone.firstChild);
|
||||
};
|
||||
|
||||
var handleInputFile = function (zone) {
|
||||
console.log('handle file');
|
||||
|
||||
var
|
||||
file = zone.querySelector('input[type="file"]').files[0],
|
||||
|
||||
reader = new FileReader()
|
||||
;
|
||||
|
||||
reader.onload = e => {
|
||||
transmitArrayBuffer(zone, e.target.result);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
|
||||
var createFilename = () => {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for (let i = 0; i < 7; i++)
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
var transmitArrayBuffer = (zone, data) => {
|
||||
var
|
||||
iv = crypto.getRandomValues(new Uint8Array(16)),
|
||||
generateTempUrlPost = zone.querySelector('input[data-async-file-upload]').dataset.generateTempUrlPost,
|
||||
suffix = createFilename(),
|
||||
jsKey, rawKey, encryptedData, uploadData
|
||||
;
|
||||
|
||||
window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ])
|
||||
.then(key => {
|
||||
console.log('key', key);
|
||||
console.log('iv', iv);
|
||||
console.log('iv to string', iv.join(','));
|
||||
jsKey = key;
|
||||
|
||||
// we register the key somwhere
|
||||
return window.crypto.subtle.exportKey('jwk', key);
|
||||
}).then(exportedKey => {
|
||||
rawKey = exportedKey;
|
||||
|
||||
console.log('rawkey', rawKey);
|
||||
console.log('data', data);
|
||||
// we start encryption
|
||||
return window.crypto.subtle.encrypt({ name: algo, iv: iv}, jsKey, data);
|
||||
})
|
||||
.then(encrypted => {
|
||||
console.log('encrypted', encrypted);
|
||||
|
||||
encryptedData = encrypted;
|
||||
|
||||
// we get the url and parameters to upload document
|
||||
return window.fetch(generateTempUrlPost);
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(encryptedData);
|
||||
console.log(data);
|
||||
var
|
||||
formData = new FormData();
|
||||
|
||||
uploadData = data;
|
||||
|
||||
formData.append("redirect", data.redirect);
|
||||
formData.append("max_file_size", data.max_file_size);
|
||||
formData.append("max_file_count", data.max_file_count);
|
||||
formData.append("expires", data.expires);
|
||||
formData.append("signature", data.signature);
|
||||
formData.append("file", new Blob([ encryptedData ]), suffix);
|
||||
|
||||
console.log('filename', suffix);
|
||||
console.log('formData', formData);
|
||||
|
||||
return window.fetch(data.url, {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: formData
|
||||
});
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
console.log('sent');
|
||||
|
||||
storeDataInForm(zone, suffix, rawKey, iv, uploadData);
|
||||
|
||||
} else {
|
||||
console.log('response', response);
|
||||
throw new Error("error while sending data");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
|
||||
// return window.crypto.subtle.importKey('jwk', rawKey, { name: algo, iv: iv }, false, [ "decrypt"]);
|
||||
//
|
||||
// .then(key => {
|
||||
// console.log('decrypt');
|
||||
// console.log(key);
|
||||
//
|
||||
// return window.crypto.subtle.decrypt({ name: algo, iv: iv }, key, encryptedData);
|
||||
// })
|
||||
// .then(decrypted => {
|
||||
// console.log('decrypted');
|
||||
// decrypt(zone, decrypted, 'decrypted');
|
||||
// })
|
||||
|
||||
;
|
||||
};
|
||||
|
||||
var storeDataInForm = (zone, suffix, jskey, iv, uploaddata) => {
|
||||
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]')
|
||||
;
|
||||
|
||||
inputKey.value = JSON.stringify(jskey);
|
||||
inputIv.value = JSON.stringify(iv);
|
||||
inputObject.value = uploaddata.prefix + suffix;
|
||||
};
|
||||
|
||||
var decrypt = (zone, arraybuffer, name) => {
|
||||
console.log('arraybuffer', arraybuffer);
|
||||
var
|
||||
link = document.createElement('a'),
|
||||
data = new Blob([ arraybuffer ])
|
||||
;
|
||||
|
||||
link.innerHTML = name;
|
||||
|
||||
link.href = window.URL.createObjectURL(data);
|
||||
link.download = 'file';
|
||||
|
||||
zone.appendChild(link);
|
||||
};
|
||||
|
||||
|
||||
window.addEventListener('load', function(e) {
|
||||
searchForZones(document);
|
||||
});
|
||||
|
||||
|
7
Resources/views/Form/fields.html.twig
Normal file
7
Resources/views/Form/fields.html.twig
Normal file
@ -0,0 +1,7 @@
|
||||
{% block stored_object_widget %}
|
||||
<div data-stored-object="data-stored-object">
|
||||
{{ form_widget(form.filename) }}
|
||||
{{ form_widget(form.keyInfos, { 'attr': { 'data-stored-object-key': 1 } }) }}
|
||||
{{ form_widget(form.iv, { 'attr': { 'data-stored-object-iv': 1 } }) }}
|
||||
</div>
|
||||
{% endblock %}
|
3
Resources/views/Macro/macro.html.twig
Normal file
3
Resources/views/Macro/macro.html.twig
Normal file
@ -0,0 +1,3 @@
|
||||
{% macro download_button(storedObject) %}
|
||||
<a class="sc-button bt-download" data-download-button data-key="{{ storedObject.keyInfos|json_encode|escape('html_attr') }}" data-iv="{{ storedObject.iv|json_encode|escape('html_attr') }}" data-temp-url-get="{{ storedObject|file_url }}">{{ 'Download'|trans }}</a>
|
||||
{% endmacro %}
|
@ -19,8 +19,14 @@
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
|
||||
{% block title %}{{ 'Documents for %name%'|trans({ '%name%': person.firstName|capitalize ~ ' ' ~ person.lastName } )|capitalize }}{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ asset('build/async_upload.js') }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
<h1>{{ 'Document for %name%'|trans({ '%name%': person.firstName|capitalize ~ ' ' ~ person.lastName } )|capitalize }}</h1>
|
||||
|
||||
@ -42,6 +48,9 @@
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
|
||||
<li>
|
||||
{{ m.download_button(document.object) }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="sc-button">
|
||||
{{ 'See'|trans }}
|
||||
|
@ -38,3 +38,7 @@
|
||||
</ul>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ asset('build/async_upload.js') }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
5
chill.webpack.config.js
Normal file
5
chill.webpack.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = function(encore) {
|
||||
let file = __dirname + '/Resources/public/module/async_upload/index.js';
|
||||
|
||||
encore.addEntry('async_upload', file);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user