mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'ChillDocStore/sf4' into sf4
This commit is contained in:
commit
f28f34690b
1
src/Bundle/ChillDocStore/.gitignore
vendored
Normal file
1
src/Bundle/ChillDocStore/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/vendor/
|
25
src/Bundle/ChillDocStore/.gitlab-ci.yml
Normal file
25
src/Bundle/ChillDocStore/.gitlab-ci.yml
Normal file
@ -0,0 +1,25 @@
|
||||
.test_definition: &test_definition
|
||||
services:
|
||||
- chill/database:latest
|
||||
before_script:
|
||||
- composer config github-oauth.github.com $GITHUB_TOKEN
|
||||
- composer install
|
||||
- cp Resources/test/Fixtures/App/app/config/parameters.gitlab-ci.yml Resources/test/Fixtures/App/app/config/parameters.yml
|
||||
- php Resources/test/Fixtures/App/app/console --env=test cache:warmup
|
||||
- php Resources/test/Fixtures/App/app/console doctrine:migrations:migrate --env=test --no-interaction
|
||||
- php Resources/test/Fixtures/App/app/console doctrine:fixtures:load --env=test --no-interaction
|
||||
|
||||
|
||||
stages:
|
||||
- deploy
|
||||
|
||||
deploy-packagist:
|
||||
stage: deploy
|
||||
image: chill/ci-image:php-7.2
|
||||
before_script:
|
||||
# test that PACKAGIST USERNAME and PACKAGIST_TOKEN variable are set
|
||||
- if [ -z ${PACKAGIST_USERNAME+x} ]; then echo "Please set PACKAGIST_USERNAME variable"; exit -1; fi
|
||||
- if [ -z ${PACKAGIST_TOKEN+x} ]; then echo "Please set PACKAGIST_TOKEN variable"; exit -1; fi
|
||||
script:
|
||||
- STATUSCODE=$(curl -XPOST -H'content-type:application/json' "https://packagist.org/api/update-package?username=$PACKAGIST_USERNAME&apiToken=$PACKAGIST_TOKEN" -d"{\"repository\":{\"url\":\"$CI_PROJECT_URL.git\"}}" --silent --output /dev/stderr --write-out "%{http_code}")
|
||||
- if [ $STATUSCODE = "202" ]; then exit 0; else exit $STATUSCODE; fi
|
39
src/Bundle/ChillDocStore/CHANGELOG.md
Normal file
39
src/Bundle/ChillDocStore/CHANGELOG.md
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
Version 1.5.1
|
||||
=============
|
||||
|
||||
- adding .gitlab-ci to upgrade automatically packagist
|
||||
- adding fixtures for ACL and DocumentCategory
|
||||
|
||||
Version 1.5.2
|
||||
=============
|
||||
|
||||
- fix some missing translations on update / create document and "any document" in list
|
||||
- use dropzone to upload a document with a better UI
|
||||
|
||||
You must add `"dropzone": "^5.5.1"` to your dependencies in `packages.json` at the root project.
|
||||
|
||||
Version 1.5.3
|
||||
=============
|
||||
|
||||
- the javascript for uploading a file now works within collections, listening to collection events.
|
||||
|
||||
Version 1.5.4
|
||||
=============
|
||||
|
||||
- replace default message on download button below dropzone ;
|
||||
- launch event when dropzone is initialized, to allow to customize events on dropzone;
|
||||
- add privacy events to document index / show
|
||||
- add privacy events to document edit / update
|
||||
- remove dump message
|
||||
|
||||
Version 1.5.5
|
||||
=============
|
||||
|
||||
- add button to remove existing document in form, and improve UI in this part
|
||||
- fix error when document is removed in form
|
||||
|
||||
Master branch
|
||||
=============
|
||||
|
||||
- fix capitalization of person document pages
|
9
src/Bundle/ChillDocStore/ChillDocStoreBundle.php
Normal file
9
src/Bundle/ChillDocStore/ChillDocStoreBundle.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class ChillDocStoreBundle extends Bundle
|
||||
{
|
||||
}
|
30
src/Bundle/ChillDocStore/Controller/AdminController.php
Normal file
30
src/Bundle/ChillDocStore/Controller/AdminController.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
||||
/**
|
||||
* Class AdminController
|
||||
*
|
||||
* @package Chill\DocStoreBundle\Controller
|
||||
*/
|
||||
class AdminController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->render('ChillDocStoreBundle:Admin:layout.html.twig');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
*/
|
||||
public function redirectToAdminIndexAction()
|
||||
{
|
||||
return $this->redirectToRoute('chill_main_admin_central');
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Controller;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Form\DocumentCategoryType;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* Class DocumentCategoryController
|
||||
*
|
||||
* @package Chill\DocStoreBundle\Controller
|
||||
* @Route("/{_locale}/admin/document/category")
|
||||
*/
|
||||
class DocumentCategoryController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="document_category_index", methods="GET")
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$categories = $em->getRepository("ChillDocStoreBundle:DocumentCategory")->findAll();
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:DocumentCategory:index.html.twig',
|
||||
['document_categories' => $categories]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/new", name="document_category_new", methods="GET|POST")
|
||||
*/
|
||||
public function new(Request $request): Response
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$documentCategory = new DocumentCategory();
|
||||
$documentCategory
|
||||
->setBundleId('Chill\DocStoreBundle\ChillDocStoreBundle');
|
||||
$documentCategory
|
||||
->setIdInsideBundle(
|
||||
$em->getRepository("ChillDocStoreBundle:DocumentCategory")
|
||||
->nextIdInsideBundle());
|
||||
$documentCategory
|
||||
->setDocumentClass(PersonDocument::class);
|
||||
|
||||
$form = $this->createForm(DocumentCategoryType::class, $documentCategory);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($documentCategory);
|
||||
$em->flush();
|
||||
|
||||
return $this->redirectToRoute('document_category_index');
|
||||
} else {
|
||||
$documentCategory->setBundleId(
|
||||
'Chill\DocStoreBundle\ChillDocStoreBundle');
|
||||
}
|
||||
|
||||
return $this->render('ChillDocStoreBundle:DocumentCategory:new.html.twig', [
|
||||
'document_category' => $documentCategory,
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{bundleId}/{idInsideBundle}", name="document_category_show", methods="GET")
|
||||
*/
|
||||
public function show($bundleId, $idInsideBundle): Response
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$documentCategory = $em
|
||||
->getRepository("ChillDocStoreBundle:DocumentCategory")
|
||||
->findOneBy(
|
||||
['bundleId' => $bundleId, 'idInsideBundle' => $idInsideBundle]);
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:DocumentCategory:show.html.twig',
|
||||
['document_category' => $documentCategory]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{bundleId}/{idInsideBundle}/edit", name="document_category_edit", methods="GET|POST")
|
||||
*/
|
||||
public function edit(Request $request, $bundleId, $idInsideBundle): Response
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$documentCategory = $em
|
||||
->getRepository("ChillDocStoreBundle:DocumentCategory")
|
||||
->findOneBy(
|
||||
['bundleId' => $bundleId, 'idInsideBundle' => $idInsideBundle]);
|
||||
|
||||
$form = $this->createForm(DocumentCategoryType::class, $documentCategory);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
return $this->redirectToRoute('document_category_index', [
|
||||
'bundleId' => $documentCategory->getBundleId(),
|
||||
'idInsideBundle' => $documentCategory->getIdInsideBundle(),]);
|
||||
}
|
||||
|
||||
return $this->render('ChillDocStoreBundle:DocumentCategory:edit.html.twig', [
|
||||
'document_category' => $documentCategory,
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{bundleId}/{idInsideBundle}", name="document_category_delete", methods="DELETE")
|
||||
*/
|
||||
public function delete(Request $request, $bundleId, $idInsideBundle): Response
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$documentCategory = $em
|
||||
->getRepository("ChillDocStoreBundle:DocumentCategory")
|
||||
->findOneBy(
|
||||
['bundleId' => $bundleId, 'idInsideBundle' => $idInsideBundle]);
|
||||
|
||||
if ($this->isCsrfTokenValid('delete'.$bundleId.$idInsideBundle, $request->request->get('_token'))) {
|
||||
$em->remove($documentCategory);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('document_category_index');
|
||||
}
|
||||
}
|
240
src/Bundle/ChillDocStore/Controller/DocumentPersonController.php
Normal file
240
src/Bundle/ChillDocStore/Controller/DocumentPersonController.php
Normal file
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Controller;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Form\PersonDocumentType;
|
||||
use Chill\DocStoreBundle\Repository\DocumentRepository;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Class DocumentPersonController
|
||||
*
|
||||
* @package Chill\DocStoreBundle\Controller
|
||||
* @Route("/{_locale}/person/{person}/document")
|
||||
*
|
||||
* TODO faire un controller abstrait ?
|
||||
*/
|
||||
class DocumentPersonController extends AbstractController
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
* DocumentPersonController constructor.
|
||||
|
||||
* @param TranslatorInterface $translator
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
* @param AuthorizationHelper $authorizationHelper
|
||||
*/
|
||||
public function __construct(
|
||||
TranslatorInterface $translator,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
AuthorizationHelper $authorizationHelper
|
||||
) {
|
||||
$this->translator = $translator;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/", name="person_document_index", methods="GET")
|
||||
*/
|
||||
public function index(Person $person): Response
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException('Person not found');
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
|
||||
|
||||
$reachableScopes = $this->authorizationHelper
|
||||
->getReachableScopes(
|
||||
$this->getUser(), new Role(PersonDocumentVoter::SEE),
|
||||
$person->getCenter());
|
||||
|
||||
$documents = $em
|
||||
->getRepository("ChillDocStoreBundle:PersonDocument")
|
||||
->findBy(
|
||||
array('person' => $person, 'scope' => $reachableScopes),
|
||||
array('date' => 'DESC')
|
||||
);
|
||||
|
||||
$event = new PrivacyEvent($person, array(
|
||||
'element_class' => PersonDocument::class,
|
||||
'action' => 'index'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:PersonDocument:index.html.twig',
|
||||
[
|
||||
'documents' => $documents,
|
||||
'person' => $person
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/new", name="person_document_new", methods="GET|POST")
|
||||
*/
|
||||
public function new(Request $request, Person $person): Response
|
||||
{
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException('person not found');
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
|
||||
|
||||
$document = new PersonDocument();
|
||||
$document->setUser($this->getUser());
|
||||
$document->setPerson($person);
|
||||
$document->setDate(new \DateTime('Now'));
|
||||
|
||||
$form = $this->createForm(PersonDocumentType::class, $document, array(
|
||||
'center' => $document->getCenter(),
|
||||
'role' => new Role('CHILL_PERSON_DOCUMENT_CREATE')
|
||||
));
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->denyAccessUnlessGranted(
|
||||
'CHILL_PERSON_DOCUMENT_CREATE', $document,
|
||||
'creation of this activity not allowed');
|
||||
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($document);
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator->trans("The document is successfully registered"));
|
||||
|
||||
return $this->redirectToRoute('person_document_index', ['person' => $person->getId()]);
|
||||
} elseif ($form->isSubmitted() and !$form->isValid()) {
|
||||
$this->addFlash('error', $this->translator->trans("This form contains errors"));
|
||||
}
|
||||
|
||||
return $this->render('ChillDocStoreBundle:PersonDocument:new.html.twig', [
|
||||
'document' => $document,
|
||||
'form' => $form->createView(),
|
||||
'person' => $person,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}", name="person_document_show", methods="GET")
|
||||
*/
|
||||
public function show(Person $person, PersonDocument $document): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_SEE', $document);
|
||||
|
||||
$event = new PrivacyEvent($person, array(
|
||||
'element_class' => PersonDocument::class,
|
||||
'element_id' => $document->getId(),
|
||||
'action' => 'show'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:PersonDocument:show.html.twig',
|
||||
['document' => $document, 'person' => $person]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}/edit", name="person_document_edit", methods="GET|POST")
|
||||
*/
|
||||
public function edit(Request $request, Person $person, PersonDocument $document): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_UPDATE', $document);
|
||||
|
||||
$document->setUser($this->getUser());
|
||||
$document->setDate(new \DateTime('Now'));
|
||||
|
||||
$form = $this->createForm(
|
||||
PersonDocumentType::class, $document, array(
|
||||
'center' => $document->getCenter(),
|
||||
'role' => new Role('CHILL_PERSON_DOCUMENT_UPDATE'),
|
||||
));
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator->trans("The document is successfully updated"));
|
||||
|
||||
$event = new PrivacyEvent($person, array(
|
||||
'element_class' => PersonDocument::class,
|
||||
'element_id' => $document->getId(),
|
||||
'action' => 'update'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'person_document_edit',
|
||||
['id' => $document->getId(), 'person' => $person->getId()]);
|
||||
|
||||
} elseif ($form->isSubmitted() and !$form->isValid()) {
|
||||
$this->addFlash('error', $this->translator->trans("This form contains errors"));
|
||||
}
|
||||
|
||||
$event = new PrivacyEvent($person, array(
|
||||
'element_class' => PersonDocument::class,
|
||||
'element_id' => $document->getId(),
|
||||
'action' => 'edit'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:PersonDocument:edit.html.twig',
|
||||
[
|
||||
'document' => $document,
|
||||
'form' => $form->createView(),
|
||||
'person' => $person,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}", name="person_document_delete", methods="DELETE")
|
||||
*/
|
||||
public function delete(Request $request, Person $person, PersonDocument $document): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_DELETE', $document);
|
||||
|
||||
if ($this->isCsrfTokenValid('delete'.$document->getId(), $request->request->get('_token'))) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->remove($document);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'person_document_index', ['person' => $person->getId()]);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Chill\DocStoreBundle\DataFixtures\ORM;
|
||||
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
|
||||
use Chill\MainBundle\Entity\RoleScope;
|
||||
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
|
||||
/**
|
||||
* Adding acl for person document
|
||||
*
|
||||
*/
|
||||
class LoadDocumentACL extends AbstractFixture implements OrderedFixtureInterface
|
||||
{
|
||||
public function getOrder()
|
||||
{
|
||||
return 35000;
|
||||
}
|
||||
|
||||
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
|
||||
$permissionsGroup = $this->getReference($permissionsGroupRef);
|
||||
printf("processing permission group %s \n", $permissionsGroup->getName());
|
||||
foreach (LoadScopes::$references as $scopeRef){
|
||||
$scope = $this->getReference($scopeRef);
|
||||
printf("processing scope %s \n", $scope->getName()['en']);
|
||||
//create permission group
|
||||
switch ($permissionsGroup->getName()) {
|
||||
case 'social':
|
||||
if ($scope->getName()['en'] === 'administrative') {
|
||||
printf("denying power on administrative \n");
|
||||
break 2; // we do not want any power on administrative
|
||||
}
|
||||
break;
|
||||
case 'administrative':
|
||||
case 'direction':
|
||||
if (in_array($scope->getName()['en'], array('administrative', 'social'))) {
|
||||
printf("denying power on %s\n", $scope->getName()['en']);
|
||||
break 2; // we do not want any power on social or administrative
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
printf("Adding Person report acl to %s "
|
||||
. "permission group, scope '%s' \n",
|
||||
$permissionsGroup->getName(), $scope->getName()['en']);
|
||||
$roleScopeUpdate = (new RoleScope())
|
||||
->setRole(PersonDocumentVoter::CREATE)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeUpdate);
|
||||
$roleScopeCreate = (new RoleScope())
|
||||
->setRole(PersonDocumentVoter::UPDATE)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeCreate);
|
||||
$roleScopeDelete = (new RoleScope())
|
||||
->setRole(PersonDocumentVoter::DELETE)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeDelete);
|
||||
$manager->persist($roleScopeUpdate);
|
||||
$manager->persist($roleScopeCreate);
|
||||
$manager->persist($roleScopeDelete);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\DataFixtures\ORM;
|
||||
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class LoadDocumentCategory extends AbstractFixture implements OrderedFixtureInterface
|
||||
{
|
||||
public function getOrder()
|
||||
{
|
||||
return 35010;
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
$category = (new DocumentCategory('chill-doc-store', 10))
|
||||
->setDocumentClass(\Chill\DocStoreBundle\Entity\PersonDocument::class)
|
||||
->setName([
|
||||
'fr' => "Document d'identité",
|
||||
'en' => "Identity"
|
||||
])
|
||||
;
|
||||
|
||||
$manager->persist($category);
|
||||
|
||||
$category = (new DocumentCategory('chill-doc-store', 20))
|
||||
->setDocumentClass(\Chill\DocStoreBundle\Entity\PersonDocument::class)
|
||||
->setName([
|
||||
'fr' => "Courrier reçu",
|
||||
'en' => "Received email"
|
||||
])
|
||||
;
|
||||
|
||||
$manager->persist($category);
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
|
||||
/**
|
||||
* This is the class that loads and manages your bundle configuration
|
||||
*
|
||||
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
|
||||
*/
|
||||
class ChillDocStoreExtension extends Extension implements PrependExtensionInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||
$loader->load('services.yaml');
|
||||
$loader->load('services/media.yaml');
|
||||
$loader->load('services/controller.yaml');
|
||||
$loader->load('services/menu.yaml');
|
||||
$loader->load('services/fixtures.yaml');
|
||||
$loader->load('services/form.yaml');
|
||||
}
|
||||
|
||||
public function prepend(ContainerBuilder $container)
|
||||
{
|
||||
$this->prependRoute($container);
|
||||
$this->prependAuthorization($container);
|
||||
$this->prependTwig($container);
|
||||
}
|
||||
|
||||
protected function prependRoute(ContainerBuilder $container)
|
||||
{
|
||||
//declare routes for task bundle
|
||||
$container->prependExtensionConfig('chill_main', array(
|
||||
'routing' => array(
|
||||
'resources' => array(
|
||||
'@ChillDocStoreBundle/config/routes.yaml',
|
||||
'@ChampsLibresAsyncUploaderBundle/config/routes.yaml'
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
protected function prependAuthorization(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('security', array(
|
||||
'role_hierarchy' => array(
|
||||
PersonDocumentVoter::UPDATE => [PersonDocumentVoter::SEE_DETAILS],
|
||||
PersonDocumentVoter::CREATE => [PersonDocumentVoter::SEE_DETAILS],
|
||||
PersonDocumentVoter::DELETE => [PersonDocumentVoter::SEE_DETAILS],
|
||||
PersonDocumentVoter::SEE_DETAILS => [PersonDocumentVoter::SEE],
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
protected function prependTwig(ContainerBuilder $container)
|
||||
{
|
||||
$twigConfig = array(
|
||||
'form_themes' => array('@ChillDocStore/Form/fields.html.twig')
|
||||
);
|
||||
$container->prependExtensionConfig('twig', $twigConfig);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
/**
|
||||
* This is the class that validates and merges configuration from your app/config files
|
||||
*
|
||||
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_doc_store');
|
||||
$rootNode = $treeBuilder->getRootNode('chill_doc_store');
|
||||
|
||||
// Here you should define the parameters that are allowed to
|
||||
// configure your bundle. See the documentation linked above for
|
||||
// more information on that topic.
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
171
src/Bundle/ChillDocStore/Entity/Document.php
Normal file
171
src/Bundle/ChillDocStore/Entity/Document.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* @ORM\MappedSuperclass()
|
||||
*/
|
||||
class Document implements HasScopeInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id()
|
||||
* @ORM\GeneratedValue()
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
* @Assert\Length(
|
||||
* min=2, max=250
|
||||
* )
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
*/
|
||||
private $description = '';
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Chill\DocStoreBundle\Entity\DocumentCategory")
|
||||
* @ORM\JoinColumns({
|
||||
* @ORM\JoinColumn(name="category_bundle_id", referencedColumnName="bundle_id"),
|
||||
* @ORM\JoinColumn(name="category_id_inside_bundle", referencedColumnName="id_inside_bundle")
|
||||
* })
|
||||
*/
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="Chill\DocStoreBundle\Entity\StoredObject",
|
||||
* cascade={"persist"}
|
||||
* )
|
||||
* @Assert\Valid()
|
||||
* @Assert\NotNull(
|
||||
* message="Upload a document"
|
||||
* )
|
||||
*/
|
||||
private $object;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
|
||||
* @var \Chill\MainBundle\Entity\Scope The document's center
|
||||
*/
|
||||
private $scope;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
|
||||
* @var \Chill\PersonBundle\Entity\user The user who encoded the exif_read_data
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $date;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): self
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription($description): self
|
||||
{
|
||||
$this->description = (string) $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DocumentCategory
|
||||
*/
|
||||
public function getCategory(): ?DocumentCategory
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
public function setCategory(DocumentCategory $category): self
|
||||
{
|
||||
$this->category = $category;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scope
|
||||
*
|
||||
* @return \Chill\MainBundle\Entity\Scope
|
||||
*/
|
||||
public function getScope()
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
public function setScope($scope): self
|
||||
{
|
||||
$this->scope = $scope;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser($user): self
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function setDate(\DateTimeInterface $date): self
|
||||
{
|
||||
$this->date = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObject(): ?StoredObject
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
public function setObject(StoredObject $object = null)
|
||||
{
|
||||
$this->object = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
93
src/Bundle/ChillDocStore/Entity/DocumentCategory.php
Normal file
93
src/Bundle/ChillDocStore/Entity/DocumentCategory.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table("chill_doc.document_category")
|
||||
* @ORM\Entity(repositoryClass="Chill\DocStoreBundle\EntityRepository\DocumentCategoryRepository")
|
||||
*/
|
||||
class DocumentCategory
|
||||
{
|
||||
/**
|
||||
* @ORM\Id()
|
||||
* @ORM\Column(type="string", name="bundle_id")
|
||||
* @var string The id of the bundle that has create the category (i.e. 'person', 'activity', ....)
|
||||
*/
|
||||
private $bundleId;
|
||||
|
||||
/**
|
||||
* @ORM\Id()
|
||||
* @ORM\Column(type="integer", name="id_inside_bundle")
|
||||
* @var int The id which is unique inside the bundle
|
||||
*/
|
||||
private $idInsideBundle;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", name="document_class")
|
||||
* @var string The class of the document (ie Chill\DocStoreBundle\PersonDocument)
|
||||
*/
|
||||
private $documentClass;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json_array")
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct($bundleId, $idInsideBundle)
|
||||
{
|
||||
$this->bundleId = $bundleId;
|
||||
$this->idInsideBundle = $idInsideBundle;
|
||||
}
|
||||
|
||||
public function getBundleId() // ::class BundleClass (FQDN)
|
||||
{
|
||||
return $this->bundleId;
|
||||
}
|
||||
|
||||
public function getIdInsideBundle()
|
||||
{
|
||||
return $this->idInsideBundle;
|
||||
}
|
||||
|
||||
public function getDocumentClass()
|
||||
{
|
||||
return $this->documentClass;
|
||||
}
|
||||
|
||||
public function setDocumentClass($documentClass): self
|
||||
{
|
||||
$this->documentClass = $documentClass;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName($locale = null)
|
||||
{
|
||||
if ($locale) {
|
||||
if (isset($this->name[$locale])) {
|
||||
return $this->name[$locale];
|
||||
} else {
|
||||
foreach ($this->name as $name) {
|
||||
if (!empty($name)) {
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
} else {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
public function setName($name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
46
src/Bundle/ChillDocStore/Entity/PersonDocument.php
Normal file
46
src/Bundle/ChillDocStore/Entity/PersonDocument.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Table("chill_doc.person_document")
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class PersonDocument extends Document implements HasCenterInterface, HasScopeInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\Person")
|
||||
* @var Person
|
||||
*/
|
||||
private $person;
|
||||
|
||||
/**
|
||||
* Get person
|
||||
*
|
||||
* @return \Chill\MainBundle\Entity\Person
|
||||
*/
|
||||
public function getPerson()
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function setPerson($person): self
|
||||
{
|
||||
$this->person = $person;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCenter()
|
||||
{
|
||||
return $this->getPerson()->getCenter();
|
||||
}
|
||||
}
|
157
src/Bundle/ChillDocStore/Entity/StoredObject.php
Normal file
157
src/Bundle/ChillDocStore/Entity/StoredObject.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
||||
use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists;
|
||||
|
||||
/**
|
||||
* Represent a document stored in an object store
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table("chill_doc.stored_object")
|
||||
* @AsyncFileExists(
|
||||
* message="The file is not stored properly"
|
||||
* )
|
||||
*/
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Chill\DocStoreBundle\EntityRepository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Chill\CustomFieldsBundle\Entity\CustomFieldLongChoice\Option;
|
||||
|
||||
/**
|
||||
* Get an available idInsideBUndle
|
||||
*/
|
||||
class DocumentCategoryRepository extends EntityRepository
|
||||
{
|
||||
public function nextIdInsideBundle()
|
||||
{
|
||||
$array_res = $this->getEntityManager()
|
||||
->createQuery(
|
||||
'SELECT MAX(c.idInsideBundle) + 1 FROM ChillDocStoreBundle:DocumentCategory c')
|
||||
->getSingleResult();
|
||||
|
||||
return $array_res[1] ?: 0;
|
||||
}
|
||||
}
|
50
src/Bundle/ChillDocStore/Form/DocumentCategoryType.php
Normal file
50
src/Bundle/ChillDocStore/Form/DocumentCategoryType.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Form;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
|
||||
class DocumentCategoryType extends AbstractType
|
||||
{
|
||||
private $chillBundlesFlipped;
|
||||
|
||||
public function __construct($kernelBundles)
|
||||
{
|
||||
// TODO faire un service dans CHillMain
|
||||
foreach ($kernelBundles as $key => $value) {
|
||||
if(substr($key, 0, 5) === 'Chill') {
|
||||
$this->chillBundlesFlipped[$value] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('bundleId', ChoiceType::class, array(
|
||||
'choices' => $this->chillBundlesFlipped,
|
||||
'disabled' => true,
|
||||
))
|
||||
->add('idInsideBundle', null, array(
|
||||
'disabled' => true,
|
||||
))
|
||||
->add('documentClass', null, array(
|
||||
'disabled' => true,
|
||||
)) // cahcerh par default PersonDocument
|
||||
->add('name', TranslatableStringFormType::class)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => DocumentCategory::class,
|
||||
]);
|
||||
}
|
||||
}
|
103
src/Bundle/ChillDocStore/Form/PersonDocumentType.php
Normal file
103
src/Bundle/ChillDocStore/Form/PersonDocumentType.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Form;
|
||||
|
||||
|
||||
use Chill\DocStoreBundle\Entity\Document;
|
||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Chill\MainBundle\Form\Type\AppendScopeChoiceTypeTrait;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
|
||||
|
||||
class PersonDocumentType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* the user running this form
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var ObjectManager
|
||||
*/
|
||||
protected $om;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
)
|
||||
{
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('title', TextType::class)
|
||||
->add('description', TextareaType::class, [
|
||||
'required' => false
|
||||
])
|
||||
->add('object', StoredObjectType::class, [
|
||||
'error_bubbling' => true
|
||||
])
|
||||
->add('scope', ScopePickerType::class, [
|
||||
'center' => $options['center'],
|
||||
'role' => $options['role']
|
||||
])
|
||||
->add('date', ChillDateType::class)
|
||||
->add('category', EntityType::class, array(
|
||||
'placeholder' => 'Choose a document category',
|
||||
'class' => 'ChillDocStoreBundle:DocumentCategory',
|
||||
'query_builder' => function (EntityRepository $er) {
|
||||
return $er->createQueryBuilder('c')
|
||||
->where('c.documentClass = :docClass')
|
||||
->setParameter('docClass', PersonDocument::class);
|
||||
},
|
||||
'choice_label' => function ($entity = null) {
|
||||
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
|
||||
},
|
||||
))
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Document::class,
|
||||
]);
|
||||
|
||||
$resolver->setRequired(['role', 'center'])
|
||||
->setAllowedTypes('role', [ \Symfony\Component\Security\Core\Role\Role::class ])
|
||||
->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ])
|
||||
;
|
||||
}
|
||||
}
|
108
src/Bundle/ChillDocStore/Form/StoredObjectType.php
Normal file
108
src/Bundle/ChillDocStore/Form/StoredObjectType.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?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;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Form type which allow to join a document
|
||||
*
|
||||
*/
|
||||
class StoredObjectType extends AbstractType
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('filename', AsyncUploaderType::class)
|
||||
->add('type', HiddenType::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']
|
||||
));
|
||||
|
||||
$builder
|
||||
->addModelTransformer(new CallbackTransformer(
|
||||
[ $this, 'transformObject'], [$this, 'reverseTransformObject']
|
||||
));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public function transformObject($object = null)
|
||||
{
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function reverseTransformObject($object)
|
||||
{
|
||||
if (NULL === $object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (NULL === $object->getFilename()) {
|
||||
// remove the original object
|
||||
$this->em->remove($object);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
87
src/Bundle/ChillDocStore/Menu/MenuBuilder.php
Normal file
87
src/Bundle/ChillDocStore/Menu/MenuBuilder.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/*
|
||||
*/
|
||||
namespace Chill\DocStoreBundle\Menu;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class MenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
public function __construct(
|
||||
TokenStorageInterface $tokenStorage,
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
TranslatorInterface $translator
|
||||
){
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
switch($menuId) {
|
||||
case 'person':
|
||||
$this->buildMenuPerson($menu, $parameters);
|
||||
break;
|
||||
default:
|
||||
throw new \LogicException("this menuid $menuId is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildMenuPerson(MenuItem $menu, array $parameters)
|
||||
{
|
||||
/* @var $person \Chill\PersonBundle\Entity\Person */
|
||||
$person = $parameters['person'];
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
|
||||
if ($this->authorizationHelper->userHasAccess($user,
|
||||
$person->getCenter(), PersonDocumentVoter::SEE)) {
|
||||
|
||||
$menu->addChild($this->translator->trans('Documents'), [
|
||||
'route' => 'person_document_index',
|
||||
'routeParameters' => [
|
||||
'person' => $person->getId()
|
||||
]
|
||||
])
|
||||
->setExtras([
|
||||
'order'=> 350
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return [ 'person' ];
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?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)
|
||||
{
|
||||
if ($data instanceof StoredObject) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function toData(AsyncFileInterface $asyncFile)
|
||||
{
|
||||
$object = $this->em
|
||||
->getRepository(StoredObject::class)
|
||||
->findByFilename($asyncFile->getObjectName())
|
||||
;
|
||||
|
||||
return $object ?? (new StoredObject())
|
||||
->setFilename($asyncFile->getObjectName())
|
||||
;
|
||||
}
|
||||
}
|
40
src/Bundle/ChillDocStore/Object/PersistenceChecker.php
Normal file
40
src/Bundle/ChillDocStore/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();
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
var mime = require('mime-types');
|
||||
|
||||
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) => {
|
||||
var
|
||||
keyData = JSON.parse(button.dataset.key),
|
||||
ivData = JSON.parse(button.dataset.iv),
|
||||
iv = new Uint8Array(ivData),
|
||||
urlGenerator = button.dataset.tempUrlGetGenerator,
|
||||
hasFilename = 'filename' in button.dataset,
|
||||
filename = button.dataset.filename,
|
||||
labelPreparing = button.dataset.labelPreparing,
|
||||
labelReady = button.dataset.labelReady,
|
||||
mimeType = button.dataset.mimeType,
|
||||
extension = mime.extension(mimeType),
|
||||
decryptError = "Error while decrypting file",
|
||||
fetchError = "Error while fetching file",
|
||||
key, url
|
||||
;
|
||||
|
||||
button.textContent = labelPreparing;
|
||||
|
||||
window.fetch(urlGenerator)
|
||||
.then((r) => {
|
||||
if (r.ok) {
|
||||
return r.json();
|
||||
} else {
|
||||
throw new Error("error while downloading url " + r.status + " " + r.statusText);
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
url = data.url;
|
||||
|
||||
return window.crypto.subtle.importKey('jwk', keyData, { name: algo, iv: iv}, false, ['decrypt']);
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("error while importing key");
|
||||
console.error(e);
|
||||
button.appendChild(document.createTextNode(decryptError));
|
||||
})
|
||||
.then(nKey => {
|
||||
key = nKey;
|
||||
|
||||
return window.fetch(url);
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("error while fetching data");
|
||||
console.error(e);
|
||||
button.textContent = "";
|
||||
button.appendChild(document.createTextNode(fetchError));
|
||||
})
|
||||
.then(r => {
|
||||
if (r.ok) {
|
||||
return r.arrayBuffer();
|
||||
} else {
|
||||
throw new Error(r.status + r.statusText);
|
||||
}
|
||||
})
|
||||
.then(buffer => {
|
||||
return window.crypto.subtle.decrypt({ name: algo, iv: iv }, key, buffer);
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("error while importing key");
|
||||
console.error(e);
|
||||
button.textContent = "";
|
||||
button.appendChild(document.createTextNode(decryptError));
|
||||
})
|
||||
.then(decrypted => {
|
||||
var
|
||||
blob = new Blob([decrypted], { type: mimeType }),
|
||||
url = window.URL.createObjectURL(blob)
|
||||
;
|
||||
button.href = url;
|
||||
button.target = '_blank';
|
||||
button.type = mimeType;
|
||||
button.textContent = labelReady;
|
||||
if (hasFilename) {
|
||||
button.download = filename;
|
||||
if (extension !== false) {
|
||||
button.download = button.download + '.' + extension;
|
||||
}
|
||||
}
|
||||
button.removeEventListener('click', onClick);
|
||||
button.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
button.textContent = "";
|
||||
button.appendChild(document.createTextNode("error while handling decrypted file"));
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
window.addEventListener('load', function(e) {
|
||||
initializeButtons(e.target);
|
||||
});
|
||||
|
||||
module.exports = initializeButtons;
|
@ -0,0 +1,2 @@
|
||||
require('./uploader.js');
|
||||
require('./downloader.js');
|
@ -0,0 +1,34 @@
|
||||
// override dropzone from dropzoneJS
|
||||
.dropzone {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.dz-preview {
|
||||
display: initial;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
.dz-image {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.dz-details, .dz-progress, .dz-success-mark, .dz-error-mark {
|
||||
position: initial;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chill-dropzone__below-zone {
|
||||
|
||||
display: flex;
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.sc-button.dz-bt-below-dropzone {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
@ -0,0 +1,372 @@
|
||||
var algo = 'AES-CBC';
|
||||
var Dropzone = require('dropzone');
|
||||
var initializeDownload = require('./downloader.js');
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* define a dropzone for chill usage
|
||||
*
|
||||
* An event is launched when dropzone is initialize, allowing to customize events
|
||||
* on dropzone :
|
||||
*
|
||||
* ```
|
||||
* window.addEventListener("chill_dropzone_initialized", (e) => {
|
||||
* // do something with dropzone:
|
||||
* e.detail.dropzone.on("success", (e) => {
|
||||
* // see https://www.dropzonejs.com/#event-success
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
|
||||
// load css
|
||||
//require('dropzone/dist/basic.css');
|
||||
require('dropzone/dist/dropzone.css');
|
||||
require('./index.scss');
|
||||
//
|
||||
|
||||
// disable dropzone autodiscover
|
||||
Dropzone.autoDiscover = false;
|
||||
|
||||
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 getUploadUrl = function(zoneData, files) {
|
||||
var
|
||||
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
|
||||
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 => {
|
||||
jsKey = key;
|
||||
|
||||
// we register the key somwhere
|
||||
return window.crypto.subtle.exportKey('jwk', key);
|
||||
}).then(exportedKey => {
|
||||
rawKey = exportedKey;
|
||||
|
||||
// we start encryption
|
||||
return window.crypto.subtle.encrypt({ name: algo, iv: iv}, jsKey, e.target.result);
|
||||
})
|
||||
.then(encrypted => {
|
||||
zoneData.crypto = {
|
||||
jsKey: jsKey,
|
||||
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
|
||||
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));
|
||||
|
||||
dropzoneI = new Dropzone(created, {
|
||||
url: function(files) {
|
||||
return getUploadUrl(zoneData, files);
|
||||
},
|
||||
dictDefaultMessage: zone.dataset.dictDefaultMessage,
|
||||
dictFileTooBig: zone.dataset.dictFileTooBig,
|
||||
dictRemoveFile: zone.dataset.dictRemoveFile,
|
||||
dictMaxFilesExceeded: zone.dataset.dictMaxFilesExceeded,
|
||||
dictCancelUpload: zone.dataset.dictCancelUpload,
|
||||
dictCancelUploadConfirm: zone.dataset.dictCancelUploadConfirm,
|
||||
dictUploadCanceled: zone.dataset.dictUploadCanceled,
|
||||
maxFiles: 1,
|
||||
addRemoveLinks: true,
|
||||
transformFile: function(file, done) {
|
||||
encryptFile(file, zoneData, done);
|
||||
},
|
||||
renameFile: function(file) {
|
||||
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);
|
||||
formData.append("max_file_count", zoneData.params.max_file_count);
|
||||
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,
|
||||
zoneData: zoneData
|
||||
}
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
|
||||
var initialize = function(zone) {
|
||||
var
|
||||
allowRemove = zone.dataset.allowRemove,
|
||||
zoneData = { zone: zone, suffix: createFilename(), allowRemove: allowRemove, old: null }
|
||||
;
|
||||
|
||||
if (hasDataInForm(zone, zoneData)) {
|
||||
insertRemoveButton(zone, zoneData);
|
||||
insertDownloadButton(zone, zoneData);
|
||||
} else {
|
||||
createZone(zone, zoneData);
|
||||
}
|
||||
};
|
||||
|
||||
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 storeDataInForm = (zone, zoneData) => {
|
||||
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;
|
||||
inputObject.value = zoneData.params.prefix + zoneData.suffix;
|
||||
|
||||
insertDownloadButton(zone);
|
||||
};
|
||||
|
||||
const restoreDataInForm = (zone, zoneData) => {
|
||||
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;
|
||||
inputObject.value = zoneData.old.obj;
|
||||
|
||||
insertDownloadButton(zone);
|
||||
};
|
||||
|
||||
const hasDataInForm = (zone, zoneData) => {
|
||||
var
|
||||
inputObject = zone.querySelector('input[data-async-file-upload]')
|
||||
;
|
||||
|
||||
return inputObject.value.length > 0;
|
||||
};
|
||||
|
||||
var removeDataInForm = (zone, zoneData) => {
|
||||
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,
|
||||
iv: inputIv.value,
|
||||
obj: inputObject.value,
|
||||
type: inputType.value
|
||||
};
|
||||
// set blank values
|
||||
inputKey.value = "";
|
||||
inputIv.value = "";
|
||||
inputType.value = "";
|
||||
inputObject.value = "";
|
||||
|
||||
insertDownloadButton(zone);
|
||||
};
|
||||
|
||||
var insertRemoveButton = (zone, zoneData) => {
|
||||
var
|
||||
removeButton = document.createElement('a'),
|
||||
cancelButton = document.createElement('a'),
|
||||
labelRemove = zone.dataset.dictRemove,
|
||||
labelCancel = 'Restaurer'
|
||||
;
|
||||
|
||||
removeButton.classList.add('sc-button', 'bt-delete');
|
||||
removeButton.textContent = labelRemove;
|
||||
|
||||
cancelButton.classList.add('sc-button');
|
||||
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);
|
||||
});
|
||||
}
|
||||
addBelowButton(cancelButton, zone, zoneData);
|
||||
//zone.appendChild(cancelButton);
|
||||
removeButton.remove();
|
||||
createZone(zone, zoneData);
|
||||
});
|
||||
|
||||
addBelowButton(removeButton, zone, zoneData);
|
||||
// zone.appendChild(removeButton);
|
||||
};
|
||||
|
||||
const removeDownloadButton = (zone, zoneData) => {
|
||||
var
|
||||
existingButtons = zone.querySelectorAll('a[data-download-button]')
|
||||
;
|
||||
|
||||
// remove existing
|
||||
existingButtons.forEach(function(b) {
|
||||
b.remove();
|
||||
});
|
||||
};
|
||||
|
||||
var insertDownloadButton = (zone, zoneData) => {
|
||||
var
|
||||
existingButtons = zone.querySelectorAll('a[data-download-button]'),
|
||||
newButton = document.createElement('a'),
|
||||
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]'),
|
||||
labelPreparing = zone.dataset.labelPreparing,
|
||||
labelQuietButton = zone.dataset.labelQuietButton,
|
||||
labelReady = zone.dataset.labelReady,
|
||||
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;
|
||||
newButton.dataset.mimeType = inputType.value;
|
||||
newButton.dataset.labelPreparing = labelPreparing;
|
||||
newButton.dataset.labelReady = labelReady;
|
||||
newButton.dataset.tempUrlGetGenerator = tempUrlGenerator + '?' + tempUrlGeneratorParams.toString();
|
||||
newButton.classList.add('sc-button', 'bt-download', 'dz-bt-below-dropzone');
|
||||
newButton.textContent = labelQuietButton;
|
||||
|
||||
addBelowButton(newButton, zone, zoneData);
|
||||
//zone.appendChild(newButton);
|
||||
initializeDownload(zone);
|
||||
};
|
||||
|
||||
window.addEventListener('load', function(e) {
|
||||
searchForZones(document);
|
||||
});
|
||||
|
||||
window.addEventListener('collection-add-entry', function(e) {
|
||||
searchForZones(e.detail.entry);
|
||||
});
|
@ -0,0 +1,31 @@
|
||||
{#
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
|
||||
|
||||
{% block vertical_menu_content %}
|
||||
{{ chill_menu('admin_docstore', {
|
||||
'layout': '@ChillDocStore/Admin/menu.html.twig',
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block layout_wvm_content %}
|
||||
{% block admin_content %}<!-- block personcontent empty -->
|
||||
<h1>{{ 'Documents configuration' |trans }}</h1>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -0,0 +1,21 @@
|
||||
{#
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillMain/Menu/verticalMenu.html.twig" %}
|
||||
{% block v_menu_title %}{{ 'Documents configuration'|trans }}{% endblock %}
|
||||
|
@ -0,0 +1,21 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
<form method="post" action="{{ path('document_category_delete', {'bundleId': document_category.bundleId, 'idInsideBundle': document_category.idInsideBundle}) }}">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ document_category.bundleId ~ document_category.idInsideBundle) }}">
|
||||
<button class="sc-button bt-delete">{{ 'Delete' | trans }}</button>
|
||||
</form>
|
@ -0,0 +1,20 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
<button class="sc-button bt-edit">{{ button_label|default('Save') }}</button>
|
||||
{{ form_end(form) }}
|
@ -0,0 +1,42 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "ChillDocStoreBundle:Admin:layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'Document category edit'|trans }}{% endblock title %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Document category edit'|trans }}</h1>
|
||||
|
||||
{# DISABLED
|
||||
{{ include('ChillDocStoreBundle:DocumentCategory:_form.html.twig', {'button_label': 'Update'}) }}
|
||||
#}
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
<ul class="record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('document_category_index') }}" class="sc-button bt-cancel">
|
||||
{{ 'Back to the category list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button class="sc-button bt-edit">{{ button_label|default('Edit')|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,62 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "ChillDocStoreBundle:Admin:layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'Document category list' | trans }}{% endblock title %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Document category list' | trans }}</h1>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'Creator bundle id' | trans }}</th>
|
||||
<th>{{ 'Internal id inside creator bundle' | trans }}</th>
|
||||
<th>{{ 'Document class' | trans }}</th>
|
||||
<th>{{ 'Name' | trans }}</th>
|
||||
<th>{{ 'Actions' | trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for document_category in document_categories %}
|
||||
<tr>
|
||||
<td>{{ document_category.bundleId }}</td>
|
||||
<td>{{ document_category.idInsideBundle }}</td>
|
||||
<td>{{ document_category.documentClass }}</td>
|
||||
<td>{{ document_category.name | localize_translatable_string}}</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ path('document_category_show', {'bundleId': document_category.bundleId, 'idInsideBundle': document_category.idInsideBundle}) }}"
|
||||
class="sc-button bt-show" title="{{ 'show' | trans }}"></a>
|
||||
<a href="{{ path('document_category_edit', {'bundleId': document_category.bundleId, 'idInsideBundle': document_category.idInsideBundle}) }}"
|
||||
class="sc-button bt-edit" title="{{ 'edit' | trans }}"></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4">{{ 'no records found' | trans }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('document_category_new') }}" class="sc-button bt-create">{{ 'Create new category' | trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock %}
|
@ -0,0 +1,42 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "ChillDocStoreBundle:Admin:layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'Create new document category' | trans }}{% endblock title %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Create new DocumentCategory' | trans }}</h1>
|
||||
|
||||
{# DISABLED
|
||||
{{ include('ChillDocStoreBundle:DocumentCategory:_form.html.twig') }}
|
||||
#}
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
<ul class="record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('document_category_index') }}" class="sc-button bt-cancel">
|
||||
{{ 'Back to the category list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button class="sc-button bt-new">{{ button_label|default('New')|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,58 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "ChillDocStoreBundle:Admin:layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'Document category show'|trans }}{% endblock title %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Document category show'|trans }}</h1>
|
||||
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{{ 'Creator bundle id' | trans }}</th>
|
||||
<td>{{ document_category.bundleId }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ 'Internal id inside creator bundle' | trans }}</th>
|
||||
<td>{{ document_category.idInsideBundle }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ 'Document class' | trans }}</th>
|
||||
<td>{{ document_category.documentClass }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ 'Name' | trans }}</th>
|
||||
<td>{{ document_category.name | localize_translatable_string }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('document_category_index') }}"
|
||||
class="sc-button bt-cancel">{{ 'Back to the category list' | trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('document_category_edit', {'bundleId': document_category.bundleId, 'idInsideBundle': document_category.idInsideBundle}) }}"
|
||||
class="sc-button bt-edit">{{ 'Edit' | trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ include('ChillDocStoreBundle:DocumentCategory:_delete_form.html.twig') }}
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock %}
|
@ -0,0 +1,22 @@
|
||||
{% block stored_object_widget %}
|
||||
<div
|
||||
data-stored-object="data-stored-object"
|
||||
data-label-preparing="{{ ('Preparing'|trans ~ '...')|escape('html_attr') }}"
|
||||
data-label-quiet-button="{{ 'Download existing file'|trans|escape('html_attr') }}"
|
||||
data-label-ready="{{ 'Ready to show'|trans|escape('html_attr') }}"
|
||||
data-dict-file-too-big="{{ 'File too big'|trans|escape('html_attr') }}"
|
||||
data-dict-default-message="{{ "Drop your file or click here"|trans|escape('html_attr') }}"
|
||||
data-dict-remove-file="{{ 'Remove file in order to upload a new one'|trans|escape('html_attr') }}"
|
||||
data-dict-max-files-exceeded="{{ 'Max files exceeded. Remove previous files'|trans|escape('html_attr') }}"
|
||||
data-dict-cancel-upload="{{ 'Cancel upload'|trans|escape('html_attr') }}"
|
||||
data-dict-cancel-upload-confirm="{{ 'Are you sure you want to cancel this upload ?'|trans|escape('html_attr') }}"
|
||||
data-dict-upload-canceled="{{ 'Upload canceled'|trans|escape('html_attr') }}"
|
||||
data-dict-remove="{{ 'Remove existing file'|trans|escape('html_attr') }}"
|
||||
data-allow-remove="{% if required %}false{% else %}true{% endif %}"
|
||||
data-temp-url-generator="{{ path('async_upload.generate_url', { 'method': 'GET' })|escape('html_attr') }}">
|
||||
{{ form_widget(form.filename) }}
|
||||
{{ form_widget(form.keyInfos, { 'attr': { 'data-stored-object-key': 1 } }) }}
|
||||
{{ form_widget(form.iv, { 'attr': { 'data-stored-object-iv': 1 } }) }}
|
||||
{{ form_widget(form.type, { 'attr': { 'data-async-file-type': 1 } }) }}
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,15 @@
|
||||
{% macro download_button(storedObject, filename = null) %}
|
||||
{% if storedObject is null %}
|
||||
<!-- No document to download -->
|
||||
{% else %}
|
||||
<a class="sc-button bt-download"
|
||||
data-label-preparing="{{ ('Preparing'|trans ~ '...')|escape('html_attr') }}"
|
||||
data-label-ready="{{ 'Ready to show'|trans|escape('html_attr') }}"
|
||||
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-generator="{{ storedObject|generate_url|escape('html_attr') }}"
|
||||
data-mime-type="{{ storedObject.type|escape('html_attr') }}" {% if filename is not null %}data-filename="{{ filename|escape('html_attr') }}"{% endif %}>
|
||||
{{ 'Download'|trans }}</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
@ -0,0 +1,5 @@
|
||||
<form method="post" action="{{ path('person_document_delete', {'id': document.id, 'person': person.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ document.id) }}">
|
||||
<button class="sc-button bt-delete">{{ 'Delete' | trans }}</button>
|
||||
</form>
|
@ -0,0 +1,64 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillPerson/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}{{ 'Editing document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
|
||||
{% block personcontent %}
|
||||
<h1>{{ 'Edit Document' | trans }}</h1>
|
||||
|
||||
{{ form_errors(form) }}
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.date) }}
|
||||
{{ form_row(form.category) }}
|
||||
{{ form_row(form.scope) }}
|
||||
{{ form_row(form.description) }}
|
||||
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="sc-button bt-cancel">
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="edit">
|
||||
<button class="sc-button bt-edit">{{ 'Edit'|trans }}</button>
|
||||
</li>
|
||||
{# {% if is_granted('CHILL_PERSON_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
{{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }}
|
||||
</li>
|
||||
{% endif %} #}
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ asset('build/async_upload.js') }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="{{ asset('build/async_upload.css') }}"/>
|
||||
{% endblock %}
|
@ -0,0 +1,83 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillPerson/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
|
||||
{% block title %}{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ asset('build/async_upload.js') }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'Title' | trans }}</th>
|
||||
<th>{{ 'Category'|trans }}</th>
|
||||
<th>{{ 'Circle' | trans }}</th>
|
||||
<th>{{ 'Actions' | trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for document in documents %}
|
||||
<tr>
|
||||
<td>{{ document.title }}</td>
|
||||
<td>{{ document.category.name|localize_translatable_string }}</td>
|
||||
<td>{{ document.scope.name|localize_translatable_string }}</td>
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="sc-button bt-show"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="sc-button bt-update"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="9" style="text-align:center;"><span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
|
||||
<ul class="record_actions">
|
||||
<li class="create">
|
||||
<a href="{{ path('person_document_new', {'person': person.id}) }}" class="sc-button bt-create">
|
||||
{{ 'Create new document' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -0,0 +1,56 @@
|
||||
{#
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "@ChillPerson/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
<h1>{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
|
||||
|
||||
{{ form_errors(form) }}
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.date) }}
|
||||
{{ form_row(form.category) }}
|
||||
{{ form_row(form.scope) }}
|
||||
{{ form_row(form.description) }}
|
||||
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="sc-button bt-cancel">
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="create">
|
||||
<button class="sc-button bt-create">{{ 'Create'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ asset('build/async_upload.js') }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="{{ asset('build/async_upload.css') }}"/>
|
||||
{% endblock %}
|
@ -0,0 +1,75 @@
|
||||
{#
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "@ChillPerson/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
|
||||
{% block title %}{{ 'Detail of document of %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{{ asset('build/async_upload.js') }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
<h1>{{ 'Document %title%' | trans({ '%title%': document.title }) }}</h1>
|
||||
|
||||
<dl class="chill_view_data">
|
||||
<dt>{{ 'Title'|trans }}</dt>
|
||||
<dd>{{ document.title }}</dd>
|
||||
|
||||
<dt>{{ 'Scope' | trans }}</dt>
|
||||
<dd>{{ document.scope.name | localize_translatable_string }}</dd>
|
||||
|
||||
<dt>{{ 'Category'|trans }}</dt>
|
||||
<dd>{{ document.category.name|localize_translatable_string }}</dd>
|
||||
|
||||
<dt>{{ 'Description' | trans }}</dt>
|
||||
<dd>
|
||||
{% if document.description is empty %}
|
||||
<span class="chill-no-data-statement">{{ 'Any description'|trans }}</span>
|
||||
{% else %}
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ document.description }}
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="sc-button bt-cancel">
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('person_document_edit', {'id': document.id, 'person': person.id}) }}" class="sc-button bt-edit">
|
||||
{{ 'Edit' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{# {{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }} #}
|
||||
{% endblock %}
|
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Champs-Libres SCRLFS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Chill\DocStoreBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||
{
|
||||
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';
|
||||
const SEE = 'CHILL_PERSON_DOCUMENT_SEE';
|
||||
const SEE_DETAILS = 'CHILL_PERSON_DOCUMENT_SEE_DETAILS';
|
||||
const UPDATE = 'CHILL_PERSON_DOCUMENT_UPDATE';
|
||||
const DELETE = 'CHILL_PERSON_DOCUMENT_DELETE';
|
||||
|
||||
/**
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var AccessDecisionManagerInterface
|
||||
*/
|
||||
protected $accessDecisionManager;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
public function __construct(
|
||||
AccessDecisionManagerInterface $accessDecisionManager,
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
LoggerInterface $logger
|
||||
)
|
||||
{
|
||||
$this->accessDecisionManager = $accessDecisionManager;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function getRoles()
|
||||
{
|
||||
return [
|
||||
self::CREATE,
|
||||
self::SEE,
|
||||
self::SEE_DETAILS,
|
||||
self::UPDATE,
|
||||
self::DELETE
|
||||
];
|
||||
}
|
||||
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if (\in_array($attribute, $this->getRoles()) && $subject instanceof PersonDocument) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($subject instanceof Person && $attribute === self::CREATE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param PersonDocument $subject
|
||||
* @param TokenInterface $token
|
||||
* @return boolean
|
||||
*/
|
||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||
{
|
||||
$this->logger->debug(sprintf("Voting from %s class", self::class));
|
||||
|
||||
if (!$token->getUser() instanceof User) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($subject instanceof PersonDocument) {
|
||||
return $this->authorizationHelper->userHasAccess($token->getUser(), $subject, $attribute);
|
||||
|
||||
} elseif ($subject instanceof Person) {
|
||||
return $this->authorizationHelper->userHasAccess($token->getUser(), $subject, $attribute);
|
||||
|
||||
} else {
|
||||
|
||||
// subject is null. We check that at least one center is reachable
|
||||
$centers = $this->authorizationHelper
|
||||
->getReachableCenters($token->getUser(), new Role($attribute));
|
||||
|
||||
return count($centers) > 0;
|
||||
}
|
||||
|
||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->authorizationHelper->userHasAccess(
|
||||
$token->getUser(),
|
||||
$subject,
|
||||
$attribute
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function isGranted($attribute, $report, $user = null)
|
||||
{
|
||||
if (! $user instanceof User){
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->helper->userHasAccess($user, $report, $attribute);
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
public function getRolesWithHierarchy()
|
||||
{
|
||||
return ['PersonDocument' => $this->getRoles() ];
|
||||
}
|
||||
}
|
3
src/Bundle/ChillDocStore/chill.webpack.config.js
Normal file
3
src/Bundle/ChillDocStore/chill.webpack.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function(encore) {
|
||||
encore.addEntry('async_upload', __dirname + '/Resources/public/module/async_upload/index.js');
|
||||
};
|
11
src/Bundle/ChillDocStore/composer.json
Normal file
11
src/Bundle/ChillDocStore/composer.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "chill-project/chill-doc-store",
|
||||
"description": "A Chill bundle to store documents",
|
||||
"type": "symfony-bundle",
|
||||
"autoload": {
|
||||
"psr-4": { "Chill\\DocStoreBundle\\" : "" }
|
||||
},
|
||||
"require": {
|
||||
},
|
||||
"license": "AGPL-3.0"
|
||||
}
|
32
src/Bundle/ChillDocStore/config/routes.yaml
Normal file
32
src/Bundle/ChillDocStore/config/routes.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
app:
|
||||
resource: "@ChillDocStoreBundle/Controller/"
|
||||
type: annotation
|
||||
|
||||
## ADMIN SECTION
|
||||
chill_docstore_admin:
|
||||
path: /{_locale}/admin/document
|
||||
controller: Chill\DocStoreBundle\Controller\AdminController::indexAction
|
||||
options:
|
||||
menus:
|
||||
admin_section:
|
||||
order: 2200
|
||||
label: "Documents configuration menu"
|
||||
icons: ['calendar']
|
||||
|
||||
chill_docstore_admin_redirect_to_admin_index:
|
||||
path: /{_locale}/admin/document_redirect_to_main
|
||||
controller: Chill\DocStoreBundle\Controller\AdminController::redirectToAdminIndexAction
|
||||
options:
|
||||
menus:
|
||||
admin_docstore:
|
||||
order: 0
|
||||
label: Main admin menu
|
||||
|
||||
chill_docstore_category_admin:
|
||||
path: /{_locale}/admin/document/category
|
||||
controller: Chill\DocStoreBundle\Controller\DocumentCategoryController::indexAction
|
||||
options:
|
||||
menus:
|
||||
admin_docstore:
|
||||
order: 90
|
||||
label: "Documents categories"
|
26
src/Bundle/ChillDocStore/config/services.yaml
Normal file
26
src/Bundle/ChillDocStore/config/services.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
parameters:
|
||||
# cl_chill_person.example.class: Chill\PersonBundle\Example
|
||||
|
||||
services:
|
||||
Chill\DocStoreBundle\Form\DocumentCategoryType:
|
||||
class: Chill\DocStoreBundle\Form\DocumentCategoryType
|
||||
arguments: ['%kernel.bundles%']
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
Chill\DocStoreBundle\Form\PersonDocumentType:
|
||||
class: Chill\DocStoreBundle\Form\PersonDocumentType
|
||||
arguments:
|
||||
- "@chill.main.helper.translatable_string"
|
||||
tags:
|
||||
- { name: form.type, alias: chill_docstorebundle_form_document }
|
||||
|
||||
Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter:
|
||||
class: Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter
|
||||
arguments:
|
||||
- "@security.access.decision_manager"
|
||||
- "@chill.main.security.authorization.helper"
|
||||
- "@logger"
|
||||
tags:
|
||||
- { name: security.voter }
|
||||
- { name: chill.role }
|
11
src/Bundle/ChillDocStore/config/services/controller.yaml
Normal file
11
src/Bundle/ChillDocStore/config/services/controller.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
services:
|
||||
Chill\DocStoreBundle\Controller\:
|
||||
resource: '../../Controller'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
Chill\DocStoreBundle\Controller\DocumentPersonController:
|
||||
arguments:
|
||||
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||
$eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
|
||||
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
|
||||
tags: ['controller.service_arguments']
|
4
src/Bundle/ChillDocStore/config/services/fixtures.yaml
Normal file
4
src/Bundle/ChillDocStore/config/services/fixtures.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
services:
|
||||
Chill\DocStoreBundle\DataFixtures\ORM\:
|
||||
resource: ../../DataFixtures/ORM
|
||||
tags: [ 'doctrine.fixture.orm' ]
|
6
src/Bundle/ChillDocStore/config/services/form.yaml
Normal file
6
src/Bundle/ChillDocStore/config/services/form.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
services:
|
||||
Chill\DocStoreBundle\Form\StoredObjectType:
|
||||
arguments:
|
||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||
tags:
|
||||
- { name: form.type }
|
9
src/Bundle/ChillDocStore/config/services/media.yaml
Normal file
9
src/Bundle/ChillDocStore/config/services/media.yaml
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'
|
8
src/Bundle/ChillDocStore/config/services/menu.yaml
Normal file
8
src/Bundle/ChillDocStore/config/services/menu.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
services:
|
||||
Chill\DocStoreBundle\Menu\MenuBuilder:
|
||||
arguments:
|
||||
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
|
||||
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
|
||||
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||
tags:
|
||||
- { name: 'chill.menu_builder' }
|
@ -0,0 +1,39 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\DocStore;
|
||||
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Create schema for chill_doc
|
||||
*/
|
||||
final class Version20180605102533 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 SCHEMA chill_doc');
|
||||
$this->addSql('CREATE SEQUENCE chill_doc.person_document_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_doc.document_category (bundle_id VARCHAR(255) NOT NULL, id_inside_bundle INT NOT NULL, document_class VARCHAR(255) NOT NULL, name JSON NOT NULL, PRIMARY KEY(bundle_id, id_inside_bundle))');
|
||||
$this->addSql('COMMENT ON COLUMN chill_doc.document_category.name IS \'(DC2Type:json_array)\'');
|
||||
$this->addSql('CREATE TABLE chill_doc.person_document (id INT NOT NULL, category_bundle_id VARCHAR(255) DEFAULT NULL, category_id_inside_bundle INT DEFAULT NULL, scope_id INT DEFAULT NULL, user_id INT DEFAULT NULL, person_id INT DEFAULT NULL, title TEXT NOT NULL, description TEXT NOT NULL, content TEXT NOT NULL, date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_41DA53C369A0BE36EF62EFC ON chill_doc.person_document (category_bundle_id, category_id_inside_bundle)');
|
||||
$this->addSql('CREATE INDEX IDX_41DA53C682B5931 ON chill_doc.person_document (scope_id)');
|
||||
$this->addSql('CREATE INDEX IDX_41DA53CA76ED395 ON chill_doc.person_document (user_id)');
|
||||
$this->addSql('CREATE INDEX IDX_41DA53C217BBB47 ON chill_doc.person_document (person_id)');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C369A0BE36EF62EFC FOREIGN KEY (category_bundle_id, category_id_inside_bundle) REFERENCES chill_doc.document_category (bundle_id, id_inside_bundle) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C682B5931 FOREIGN KEY (scope_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53CA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP SCHEMA chill_doc CASCADE');
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\DocStore;
|
||||
|
||||
use Doctrine\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');
|
||||
}
|
||||
}
|
53
src/Bundle/ChillDocStore/translations/messages.fr.yml
Normal file
53
src/Bundle/ChillDocStore/translations/messages.fr.yml
Normal file
@ -0,0 +1,53 @@
|
||||
Document: Document
|
||||
Documents: Documents
|
||||
Documents for %name%: Documents de %name%
|
||||
Preparing: En préparation
|
||||
Ready to show: Prêt à être visualisé
|
||||
Download: Télécharger
|
||||
Download existing file: Télécharger le fichier existant
|
||||
Create new document: Créer un nouveau document
|
||||
New document for %name%: Nouveau document pour %name%
|
||||
Editing document for %name%: Modification d'un document pour %name%
|
||||
Edit Document: Modification d'un document
|
||||
Existing document: Document existant
|
||||
No document to download: Aucun document à télécharger
|
||||
'Choose a document category': Choisissez une catégorie de document
|
||||
Any document found: Aucun document trouvé
|
||||
The document is successfully registered: Le document est enregistré
|
||||
The document is successfully updated: Le document est mis à jour
|
||||
Any description: Aucune description
|
||||
|
||||
# dropzone upload
|
||||
File too big: Fichier trop volumineux
|
||||
Drop your file or click here: Cliquez ici ou faites glissez votre nouveau fichier dans cette zone
|
||||
Remove file in order to upload a new one: Supprimer ce fichier pour en insérer un autre
|
||||
Max files exceeded. Remove previous files: Nombre maximum de fichier atteint. Supprimez les précédents
|
||||
Cancel upload: Annuler le téléversement
|
||||
Are you sure you want to cancel this upload ?: Êtes-vous sûrs de vouloir annuler ce téléversement ?
|
||||
Upload canceled: Téléversement annulé
|
||||
Remove existing file: Supprimer le document existant
|
||||
|
||||
# ROLES
|
||||
PersonDocument: Documents
|
||||
CHILL_PERSON_DOCUMENT_CREATE: Ajouter un document
|
||||
CHILL_PERSON_DOCUMENT_DELETE: Supprimer un document
|
||||
CHILL_PERSON_DOCUMENT_SEE: Voir un document
|
||||
CHILL_PERSON_DOCUMENT_SEE_DETAILS: Voir le détail d'un document
|
||||
CHILL_PERSON_DOCUMENT_UPDATE: Modifier un document
|
||||
|
||||
# Admin
|
||||
Documents configuration: Configuration des documents
|
||||
Documents configuration menu: Documents
|
||||
Documents categories: Catégories de documents
|
||||
|
||||
Document category list: Catégories de documents
|
||||
Document category show: Voir la catégorie de documents
|
||||
Document category edit: Modifier la catégorie de documents
|
||||
Creator bundle id: Module
|
||||
Bundle id: Module
|
||||
Internal id inside creator bundle: Identifiant
|
||||
Id inside bundle: Identifiant
|
||||
Document class: Classe de document
|
||||
no records found:
|
||||
Create new category: Créer une nouvelle catégorie
|
||||
Back to the category list: Retour à la liste
|
2
src/Bundle/ChillDocStore/translations/validators.fr.yml
Normal file
2
src/Bundle/ChillDocStore/translations/validators.fr.yml
Normal file
@ -0,0 +1,2 @@
|
||||
The file is not stored properly: Le fichier n'est pas téléchargé correctement
|
||||
Upload a document: Téléversez un document
|
Loading…
x
Reference in New Issue
Block a user