mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'ChillReport/sf4' into sf4
This commit is contained in:
commit
a68577cc69
6
src/Bundle/ChillReport/.gitignore
vendored
Normal file
6
src/Bundle/ChillReport/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/vendor/
|
||||||
|
composer.lock
|
||||||
|
Tests/Fixtures/App/app/config/parameters.yml
|
||||||
|
*~
|
||||||
|
auth.json
|
||||||
|
/nbproject/private/
|
54
src/Bundle/ChillReport/.gitlab-ci.yml
Normal file
54
src/Bundle/ChillReport/.gitlab-ci.yml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
.test_definition: &test_definition
|
||||||
|
services:
|
||||||
|
- chill/database:latest
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- composer config github-oauth.github.com $GITHUB_TOKEN
|
||||||
|
- php -d memory_limit=-1 /usr/local/bin/composer install --no-interaction
|
||||||
|
- 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:
|
||||||
|
- test
|
||||||
|
- build-doc
|
||||||
|
- deploy-doc
|
||||||
|
|
||||||
|
test:php-7.2:
|
||||||
|
stage: test
|
||||||
|
image: chill/ci-image:php-7.2
|
||||||
|
<<: *test_definition
|
||||||
|
script: php vendor/bin/phpunit
|
||||||
|
|
||||||
|
# deploy documentation
|
||||||
|
api-doc-build:
|
||||||
|
stage: build-doc
|
||||||
|
environment: api-doc
|
||||||
|
image: chill/ci-image:php-7.2
|
||||||
|
before_script:
|
||||||
|
- mkdir api-doc
|
||||||
|
script: apigen generate --destination api-doc/$CI_BUILD_REF_NAME/$CI_PROJECT_NAME
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- "api-doc/"
|
||||||
|
name: api
|
||||||
|
expire_in: '2h'
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- tags
|
||||||
|
|
||||||
|
api-doc-deploy:
|
||||||
|
stage: deploy-doc
|
||||||
|
image: pallet/swiftclient:latest
|
||||||
|
before_script:
|
||||||
|
# test that CONTAINER_API variable is set
|
||||||
|
- if [ -z ${CONTAINER_API+x} ]; then echo "Please set CONTAINER_API variable"; exit -1; fi
|
||||||
|
# go to api-doc to have and url with PROJECT/BUILD
|
||||||
|
- cd api-doc
|
||||||
|
# upload, and keep files during 1 year
|
||||||
|
script: "swift upload --header \"X-Delete-After: 31536000\" $CONTAINER_API $CI_BUILD_REF_NAME/$CI_PROJECT_NAME"
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- tags
|
||||||
|
|
18
src/Bundle/ChillReport/CHANGELOG.md
Normal file
18
src/Bundle/ChillReport/CHANGELOG.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
Version 1.5.1
|
||||||
|
=============
|
||||||
|
|
||||||
|
- [list export] fix error "all custom fields are shown"
|
||||||
|
|
||||||
|
Version 1.5.2
|
||||||
|
=============
|
||||||
|
|
||||||
|
- add privacy events to report list / view
|
||||||
|
- add privacy events to report edit / update
|
||||||
|
|
||||||
|
Master branch
|
||||||
|
=============
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
9
src/Bundle/ChillReport/ChillReportBundle.php
Normal file
9
src/Bundle/ChillReport/ChillReportBundle.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
class ChillReportBundle extends Bundle
|
||||||
|
{
|
||||||
|
}
|
561
src/Bundle/ChillReport/Controller/ReportController.php
Normal file
561
src/Bundle/ChillReport/Controller/ReportController.php
Normal file
@ -0,0 +1,561 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
|
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\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Chill\ReportBundle\Form\ReportType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ReportController
|
||||||
|
*
|
||||||
|
* @package Chill\ReportBundle\Controller
|
||||||
|
*/
|
||||||
|
class ReportController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var EventDispatcherInterface
|
||||||
|
*/
|
||||||
|
protected $eventDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AuthorizationHelper
|
||||||
|
*/
|
||||||
|
protected $authorizationHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PaginatorFactory
|
||||||
|
*/
|
||||||
|
protected $paginator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReportController constructor.
|
||||||
|
*
|
||||||
|
* @param EventDispatcherInterface $eventDispatcher
|
||||||
|
* @param AuthorizationHelper $authorizationHelper
|
||||||
|
* @param PaginatorFactory $paginator
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
EventDispatcherInterface $eventDispatcher,
|
||||||
|
AuthorizationHelper $authorizationHelper,
|
||||||
|
PaginatorFactory $paginator
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
|
$this->authorizationHelper = $authorizationHelper;
|
||||||
|
$this->paginator = $paginator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all the report entities for a given person.
|
||||||
|
*
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param Request $request The request
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function listAction($person_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$person = $em->getRepository('ChillPersonBundle:Person')->find($person_id);
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||||
|
|
||||||
|
$reachableScopes = $this->authorizationHelper
|
||||||
|
->getReachableScopes($this->getUser(), new Role('CHILL_REPORT_SEE'),
|
||||||
|
$person->getCenter());
|
||||||
|
|
||||||
|
$total = $em
|
||||||
|
->createQuery("SELECT COUNT(r.id) FROM ChillReportBundle:Report r "
|
||||||
|
. "WHERE r.person = :person AND r.scope IN (:scopes) ")
|
||||||
|
->setParameter('person', $person)
|
||||||
|
->setParameter('scopes', $reachableScopes)
|
||||||
|
->getSingleScalarResult();
|
||||||
|
|
||||||
|
// get the PaginatorFactory
|
||||||
|
$paginator = $this->paginator->create($total);
|
||||||
|
|
||||||
|
$reports = $em->createQuery('SELECT r
|
||||||
|
FROM ChillReportBundle:Report r
|
||||||
|
WHERE r.person = :person AND r.scope IN (:scopes)
|
||||||
|
ORDER BY r.date DESC')
|
||||||
|
->setParameter('person', $person)
|
||||||
|
->setParameter('scopes', $reachableScopes)
|
||||||
|
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
|
||||||
|
->setMaxResults($paginator->getItemsPerPage())
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
|
||||||
|
$event = new PrivacyEvent($person, array(
|
||||||
|
'element_class' => Report::class,
|
||||||
|
'action' => 'list'
|
||||||
|
));
|
||||||
|
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:list.html.twig', array(
|
||||||
|
'reports' => $reports,
|
||||||
|
'person' => $person,
|
||||||
|
'paginator' => $paginator
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a form for selecting which type of report to add for a given person
|
||||||
|
*
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param Request $request The request
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function selectReportTypeAction($person_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$person = $em->getRepository('ChillPersonBundle:Person')
|
||||||
|
->find($person_id);
|
||||||
|
|
||||||
|
if ($person === NULL) {
|
||||||
|
throw $this->createNotFoundException('Person not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person, 'access denied for person view');
|
||||||
|
// check access on report creation for a dummy report
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_REPORT_CREATE',
|
||||||
|
(new Report())->setPerson($person), 'access denied for report creation');
|
||||||
|
|
||||||
|
|
||||||
|
$cFGroupId = $request->query->get('cFGroup');
|
||||||
|
|
||||||
|
if($cFGroupId) {
|
||||||
|
return $this->redirect(
|
||||||
|
$this->generateUrl('report_new',
|
||||||
|
array('person_id' => $person_id, 'cf_group_id' => $cFGroupId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$cFGroups = $em->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->findByEntity('Chill\ReportBundle\Entity\Report');
|
||||||
|
|
||||||
|
if(count($cFGroups) === 1 ){
|
||||||
|
return $this->redirect(
|
||||||
|
$this->generateUrl('report_new',
|
||||||
|
array('person_id' => $person_id, 'cf_group_id' => $cFGroups[0]->getId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
$cFGroupsChoice = array();
|
||||||
|
|
||||||
|
foreach ($cFGroups as $cFGroup) {
|
||||||
|
$cFGroupsChoice[$cFGroup->getId()] = $cFGroup->getName($request->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = $this->get('form.factory')
|
||||||
|
->createNamedBuilder(null, FormType::class, null, array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'csrf_protection' => false
|
||||||
|
))
|
||||||
|
->add('cFGroup', ChoiceType::class, array(
|
||||||
|
'choices' => array_combine(array_values($cFGroupsChoice),array_keys($cFGroupsChoice)),
|
||||||
|
))
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$person = $em->getRepository('ChillPersonBundle:Person')->find($person_id);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:select_report_type.html.twig', array(
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'person' => $person
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a form for selecting which type of report to export
|
||||||
|
* (a csv file with all the report of this type)
|
||||||
|
*
|
||||||
|
* @param Request $request The request
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function selectReportTypeForExportAction(Request $request)
|
||||||
|
{
|
||||||
|
$cFGroupId = $request->query->get('cFGroup');
|
||||||
|
|
||||||
|
if($cFGroupId) {
|
||||||
|
return $this->redirect(
|
||||||
|
$this->generateUrl('report_export_list',
|
||||||
|
array('cf_group_id' => $cFGroupId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$cFGroups = $em->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->findByEntity('Chill\ReportBundle\Entity\Report');
|
||||||
|
|
||||||
|
if(count($cFGroups) === 1 ){
|
||||||
|
return $this->redirect(
|
||||||
|
$this->generateUrl('report_export_list',
|
||||||
|
array('cf_group_id' => $cFGroups[0]->getId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
$cFGroupsChoice = array();
|
||||||
|
|
||||||
|
foreach ($cFGroups as $cFGroup) {
|
||||||
|
$cFGroupsChoice[$cFGroup->getId()] = $cFGroup->getName($request->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = $this->get('form.factory')
|
||||||
|
->createNamedBuilder(null, FormType::class, null, array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'csrf_protection' => false
|
||||||
|
))
|
||||||
|
->add('cFGroup', ChoiceType::class, array(
|
||||||
|
'choices' => array_combine(array_values($cFGroupsChoice),array_keys($cFGroupsChoice)),
|
||||||
|
))
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:select_report_type_for_export.html.twig', array(
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'layout_name' => "@ChillMain/Export/layout.html.twig"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a csv file with all the reports of a given type
|
||||||
|
*
|
||||||
|
* @param integer $cf_group_id The id of the report type to export
|
||||||
|
* @param Request $request The request
|
||||||
|
* @return A csv file with all the reports of the selected type
|
||||||
|
*/
|
||||||
|
public function exportAction($cf_group_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$cFGroup = $em->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')->find($cf_group_id);
|
||||||
|
$reports = $em->getRepository('ChillReportBundle:Report')->findByCFGroup($cFGroup);
|
||||||
|
|
||||||
|
|
||||||
|
$response = $this->render('ChillReportBundle:Report:export.csv.twig', array(
|
||||||
|
'reports' => $reports,
|
||||||
|
'cf_group' => $cFGroup
|
||||||
|
));
|
||||||
|
|
||||||
|
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
|
||||||
|
$response->headers->set('Content-Disposition', 'attachment; filename="export.csv"');
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a form for creating a new report for a given person and of a given type
|
||||||
|
*
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param integer $cf_group_id The id of the report type.
|
||||||
|
* @param Request $request The request
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function newAction($person_id, $cf_group_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$person = $em->getRepository('ChillPersonBundle:Person')->find($person_id);
|
||||||
|
$cFGroup = $em
|
||||||
|
->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->find($cf_group_id);
|
||||||
|
|
||||||
|
if ($person === NULL) {
|
||||||
|
throw $this->createNotFoundException("Person not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||||
|
// check access on report creation for a dummy report
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_REPORT_CREATE',
|
||||||
|
(new Report())->setPerson($person), 'access denied for report creation');
|
||||||
|
|
||||||
|
if ($cFGroup === NULL){
|
||||||
|
throw $this->createNotFoundException("custom fields group not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity = new Report();
|
||||||
|
$entity->setUser($this->get('security.token_storage')->getToken()->getUser());
|
||||||
|
$entity->setDate(new \DateTime('now'));
|
||||||
|
|
||||||
|
$entity->setCFGroup($cFGroup);
|
||||||
|
|
||||||
|
$form = $this->createCreateForm($entity, $person, $cFGroup);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:new.html.twig', array(
|
||||||
|
'entity' => $entity,
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'person' => $person
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new report for a given person and of a given type
|
||||||
|
*
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param integer $cf_group_id The id of the report type.
|
||||||
|
* @param Request $request The request containing the form data (from the newAction)
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function createAction($person_id, $cf_group_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$entity = new Report();
|
||||||
|
$cFGroup = $em->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->find($cf_group_id);
|
||||||
|
|
||||||
|
$person = $em->getRepository('ChillPersonBundle:Person')
|
||||||
|
->find($person_id);
|
||||||
|
|
||||||
|
if($person === NULL || $cFGroup === NULL) {
|
||||||
|
throw $this->createNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||||
|
|
||||||
|
$form = $this->createCreateForm($entity, $person, $cFGroup);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$entity->setCFGroup($cFGroup);
|
||||||
|
$entity->setPerson($person);
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_REPORT_CREATE', $entity);
|
||||||
|
|
||||||
|
$em->persist($entity);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->get('session')
|
||||||
|
->getFlashBag()
|
||||||
|
->add('success',
|
||||||
|
$this->get('translator')
|
||||||
|
->trans('Success : report created!')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->redirect($this->generateUrl('report_view',
|
||||||
|
array('person_id' => $person_id,'report_id' => $entity->getId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$this->get('session')
|
||||||
|
->getFlashBag()->add('error',
|
||||||
|
$this->get('translator')
|
||||||
|
->trans('The form is not valid. The report has not been created !')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:new.html.twig', array(
|
||||||
|
'entity' => $entity,
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'person' => $person
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a form to create a Report entity.
|
||||||
|
*
|
||||||
|
* @param Report $entity The entity
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param integer $cf_group_id The id of the report type.
|
||||||
|
*
|
||||||
|
* @return \Symfony\Component\Form\Form The form
|
||||||
|
*/
|
||||||
|
private function createCreateForm(Report $entity, Person $person, $cFGroup)
|
||||||
|
{
|
||||||
|
$form = $this->createForm(ReportType::class, $entity, array(
|
||||||
|
'action' => $this->generateUrl('report_create',
|
||||||
|
array('person_id' => $person->getId(),
|
||||||
|
'cf_group_id' => $cFGroup->getId())),
|
||||||
|
'method' => 'POST',
|
||||||
|
'cFGroup' => $cFGroup,
|
||||||
|
'role' => new Role('CHILL_REPORT_CREATE'),
|
||||||
|
'center' => $person->getCenter()
|
||||||
|
));
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and display a report.
|
||||||
|
*
|
||||||
|
* @param integer $report_id The id of the report.
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function viewAction($report_id, $person_id)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$person = $em->getRepository('ChillPersonBundle:Person')->find($person_id);
|
||||||
|
|
||||||
|
$entity = $em->getRepository('ChillReportBundle:Report')->find($report_id);
|
||||||
|
|
||||||
|
if (!$entity || !$person) {
|
||||||
|
throw $this->createNotFoundException(
|
||||||
|
$this->get('translator')->trans('Unable to find this report.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_REPORT_SEE', $entity);
|
||||||
|
|
||||||
|
|
||||||
|
$event = new PrivacyEvent($person, array(
|
||||||
|
'element_class' => Report::class,
|
||||||
|
'element_id' => $entity->getId(),
|
||||||
|
'action' => 'view'
|
||||||
|
));
|
||||||
|
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:view.html.twig', array(
|
||||||
|
'entity' => $entity,
|
||||||
|
'person' => $person,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a form to edit an existing Report entity.
|
||||||
|
*
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param integer $report_id The id of the report.
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function editAction($person_id, $report_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$report = $em->getRepository('ChillReportBundle:Report')->find($report_id);
|
||||||
|
|
||||||
|
if (!$report) {
|
||||||
|
throw $this->createNotFoundException(
|
||||||
|
$this->get('translator')->trans('Unable to find this report.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(intval($person_id) !== intval($report->getPerson()->getId())) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
$this->get('translator')->trans('This is not the report of the person.'), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_REPORT_UPDATE', $report);
|
||||||
|
|
||||||
|
$person = $report->getPerson();
|
||||||
|
|
||||||
|
$editForm = $this->createEditForm($report);
|
||||||
|
|
||||||
|
$event = new PrivacyEvent($person, array(
|
||||||
|
'element_class' => Report::class,
|
||||||
|
'element_id' => $report->getId(),
|
||||||
|
'action' => 'edit'
|
||||||
|
));
|
||||||
|
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:edit.html.twig', array(
|
||||||
|
'edit_form' => $editForm->createView(),
|
||||||
|
'person' => $person,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a form to edit a Report entity.
|
||||||
|
*
|
||||||
|
* @param Report $entity The report to edit.
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @return \Symfony\Component\Form\Form The form
|
||||||
|
*/
|
||||||
|
private function createEditForm(Report $entity)
|
||||||
|
{
|
||||||
|
$form = $this->createForm(ReportType::class, $entity, array(
|
||||||
|
'action' => $this->generateUrl('report_update',
|
||||||
|
array('person_id' => $entity->getPerson()->getId(),
|
||||||
|
'report_id' => $entity->getId())),
|
||||||
|
'method' => 'PUT',
|
||||||
|
'cFGroup' => $entity->getCFGroup(),
|
||||||
|
'role' => new Role('CHILL_REPORT_UPDATE'),
|
||||||
|
'center' => $entity->getPerson()->getCenter()
|
||||||
|
));
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web page for editing an existing report.
|
||||||
|
*
|
||||||
|
* @param integer $person_id The id of the person.
|
||||||
|
* @param integer $report_id The id of the report.
|
||||||
|
* @return Response The web page.
|
||||||
|
*/
|
||||||
|
public function updateAction($person_id, $report_id, Request $request)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$report = $em->getRepository('ChillReportBundle:Report')->find($report_id);
|
||||||
|
|
||||||
|
if (!$report) {
|
||||||
|
throw $this->createNotFoundException(
|
||||||
|
$this->get('translator')->trans('Unable to find this report.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('CHILL_REPORT_UPDATE', $report);
|
||||||
|
|
||||||
|
$editForm = $this->createEditForm($report);
|
||||||
|
$editForm->handleRequest($request);
|
||||||
|
|
||||||
|
if ($editForm->isValid()) {
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->get('session')
|
||||||
|
->getFlashBag()
|
||||||
|
->add('success',
|
||||||
|
$this->get('translator')
|
||||||
|
->trans('Success : report updated!')
|
||||||
|
);
|
||||||
|
|
||||||
|
$person = $report->getPerson();
|
||||||
|
|
||||||
|
$event = new PrivacyEvent($person, array(
|
||||||
|
'element_class' => Report::class,
|
||||||
|
'element_id' => $report->getId(),
|
||||||
|
'action' => 'update'
|
||||||
|
));
|
||||||
|
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||||
|
|
||||||
|
return $this->redirect($this->generateUrl('report_view',
|
||||||
|
array('person_id' => $report->getPerson()->getId(), 'report_id' => $report_id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->get('session')
|
||||||
|
->getFlashBag()
|
||||||
|
->add('error',
|
||||||
|
$this->get('translator')
|
||||||
|
->trans('The form is not valid. The report has not been updated !')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->render('ChillReportBundle:Report:edit.html.twig', array(
|
||||||
|
'edit_form' => $editForm->createView(),
|
||||||
|
'person' => $report->getPerson()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
222
src/Bundle/ChillReport/DataFixtures/ORM/LoadCustomField.php
Normal file
222
src/Bundle/ChillReport/DataFixtures/ORM/LoadCustomField.php
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||||
|
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load CustomField for Report into database
|
||||||
|
*/
|
||||||
|
class LoadCustomField extends AbstractFixture implements OrderedFixtureInterface
|
||||||
|
{
|
||||||
|
public function getOrder()
|
||||||
|
{
|
||||||
|
return 15001;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(ObjectManager $manager)
|
||||||
|
{
|
||||||
|
echo "loading CustomField...\n";
|
||||||
|
|
||||||
|
$cFTypes = [
|
||||||
|
array('type' => 'text', 'options' => array('maxLength' => '255')),
|
||||||
|
array('type' => 'text', 'options' => array('maxLength' => '1000')),
|
||||||
|
array('type' => 'text', 'options' => array('maxLength' => '2000')),
|
||||||
|
array('type' => 'title', 'options' => array('type' => 'title')),
|
||||||
|
array('type' => 'title', 'options' => array('type' => 'subtitle')),
|
||||||
|
array('type' => 'choice', 'options' => array(
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded'=> false,
|
||||||
|
'other' => false,
|
||||||
|
'choices'=> [
|
||||||
|
array(
|
||||||
|
'name' => array(
|
||||||
|
'fr' => 'Options 1 FR',
|
||||||
|
'nl' => 'Options 1 NL',
|
||||||
|
'en' => 'Options 1 EN'),
|
||||||
|
'active' => true,
|
||||||
|
'slug' => 'el-1-fr'),
|
||||||
|
array(
|
||||||
|
'name' => array(
|
||||||
|
'fr' => 'Options 2 FR',
|
||||||
|
'nl' => 'Options 2 NL',
|
||||||
|
'en' => 'Options 2 EN'),
|
||||||
|
'active' => true,
|
||||||
|
'slug' => 'el-2-fr'),
|
||||||
|
array(
|
||||||
|
'name' => array(
|
||||||
|
'fr' => 'Options 2 FR',
|
||||||
|
'nl' => 'Options 2 NL',
|
||||||
|
'en' => 'Options 2 EN'),
|
||||||
|
'active' => true,
|
||||||
|
'slug' => 'el-3-fr')
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
for($i=0; $i <= 25; $i++) {
|
||||||
|
echo "CustomField {$i}\n";
|
||||||
|
$cFType = $cFTypes[rand(0,sizeof($cFTypes) - 1)];
|
||||||
|
|
||||||
|
$customField = (new CustomField())
|
||||||
|
->setSlug("cf_report_{$i}")
|
||||||
|
->setType($cFType['type'])
|
||||||
|
->setOptions($cFType['options'])
|
||||||
|
->setName(array("fr" => "CustomField {$i}"))
|
||||||
|
->setOrdering(rand(0,1000) / 1000)
|
||||||
|
->setCustomFieldsGroup($this->getReference('cf_group_report_'.(rand(0,3))))
|
||||||
|
;
|
||||||
|
|
||||||
|
$manager->persist($customField);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->createExpectedFields($manager);
|
||||||
|
|
||||||
|
$manager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createExpectedFields(ObjectManager $manager)
|
||||||
|
{
|
||||||
|
//report logement
|
||||||
|
$reportLogement = $this->getReference('cf_group_report_logement');
|
||||||
|
|
||||||
|
$houseTitle = (new CustomField())
|
||||||
|
->setSlug('house_title')
|
||||||
|
->setType('title')
|
||||||
|
->setOptions(array('type' => 'title'))
|
||||||
|
->setName(array('fr' => 'Situation de logement'))
|
||||||
|
->setOrdering(10)
|
||||||
|
->setCustomFieldsGroup($reportLogement)
|
||||||
|
;
|
||||||
|
$manager->persist($houseTitle);
|
||||||
|
|
||||||
|
$hasLogement = (new CustomField())
|
||||||
|
->setSlug('has_logement')
|
||||||
|
->setName(array('fr' => 'Logement actuel'))
|
||||||
|
->setType('choice')
|
||||||
|
->setOptions(array(
|
||||||
|
'multiple' => FALSE,
|
||||||
|
'expanded' => TRUE,
|
||||||
|
'other' => TRUE,
|
||||||
|
'choices' => [
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Locataire d\' un logement'],
|
||||||
|
'slug' => 'rent_house',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Propriétaire d\' un logement'],
|
||||||
|
'slug' => 'own_house',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Par-ci, par là (amis, famille, ...)'],
|
||||||
|
'slug' => 'here-and-there',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'A la rue'],
|
||||||
|
'slug' => 'street',
|
||||||
|
'active' => true
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
))
|
||||||
|
->setOrdering(20)
|
||||||
|
->setCustomFieldsGroup($reportLogement)
|
||||||
|
;
|
||||||
|
$manager->persist($hasLogement);
|
||||||
|
|
||||||
|
$descriptionLogement = (new CustomField())
|
||||||
|
->setSlug('house-desc')
|
||||||
|
->setName(array('fr' => 'Plaintes éventuelles sur le logement'))
|
||||||
|
->setType('text')
|
||||||
|
->setOptions(['maxLength' => 1500])
|
||||||
|
->setOrdering(30)
|
||||||
|
->setCustomFieldsGroup($reportLogement)
|
||||||
|
;
|
||||||
|
$manager->persist($descriptionLogement);
|
||||||
|
|
||||||
|
|
||||||
|
//report problems
|
||||||
|
$reportEducation = $this->getReference('cf_group_report_education');
|
||||||
|
|
||||||
|
$title = (new CustomField())
|
||||||
|
->setSlug('title')
|
||||||
|
->setType('title')
|
||||||
|
->setOptions(array('type' => 'title'))
|
||||||
|
->setName(array('fr' => 'Éducation'))
|
||||||
|
->setOrdering(10)
|
||||||
|
->setCustomFieldsGroup($reportEducation)
|
||||||
|
;
|
||||||
|
$manager->persist($title);
|
||||||
|
|
||||||
|
$educationLevel = (new CustomField())
|
||||||
|
->setSlug('level')
|
||||||
|
->setName(array('fr' => 'Niveau du plus haut diplôme'))
|
||||||
|
->setType('choice')
|
||||||
|
->setOptions(array(
|
||||||
|
'multiple' => FALSE,
|
||||||
|
'expanded' => FALSE,
|
||||||
|
'other' => FALSE,
|
||||||
|
'choices' => [
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Supérieur'],
|
||||||
|
'slug' => 'superieur',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Secondaire supérieur (CESS)'],
|
||||||
|
'slug' => 'cess',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Secondaire deuxième degré ou inférieur (C2D)'],
|
||||||
|
'slug' => 'c2d',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Primaire'],
|
||||||
|
'slug' => 'low',
|
||||||
|
'active' => true
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => ['fr' => 'Aucun diplome'],
|
||||||
|
'slug' => 'no',
|
||||||
|
'active' => true
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
))
|
||||||
|
->setOrdering(20)
|
||||||
|
->setCustomFieldsGroup($reportEducation)
|
||||||
|
;
|
||||||
|
$manager->persist($educationLevel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||||
|
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load CustomFieldsGroup for Report into database
|
||||||
|
*/
|
||||||
|
class LoadCustomFieldsGroup extends AbstractFixture implements OrderedFixtureInterface
|
||||||
|
{
|
||||||
|
public function getOrder()
|
||||||
|
{
|
||||||
|
return 15000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(ObjectManager $manager)
|
||||||
|
{
|
||||||
|
echo "loading customFieldsGroup...\n";
|
||||||
|
|
||||||
|
$report = $this->createReport($manager,
|
||||||
|
array('fr' => 'Situation de logement'),
|
||||||
|
['summary_fields' => ['has_logement', 'house-desc']]);
|
||||||
|
$this->addReference(
|
||||||
|
'cf_group_report_logement',
|
||||||
|
$report
|
||||||
|
);
|
||||||
|
|
||||||
|
$report = $this->createReport($manager, array('fr' => 'Alphabétisme'));
|
||||||
|
$this->addReference('cf_group_report_education', $report);
|
||||||
|
|
||||||
|
for($i=0; $i <= 3; $i++) {
|
||||||
|
|
||||||
|
$report = $this->createReport($manager, array('fr' => 'ZZ Rapport aléatoire '.$i));
|
||||||
|
|
||||||
|
$this->addReference('cf_group_report_'.$i, $report);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$manager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a report and persist in the db
|
||||||
|
*
|
||||||
|
* @param ObjectManager $manager
|
||||||
|
* @param array $name
|
||||||
|
* @return CustomFieldsGroup
|
||||||
|
*/
|
||||||
|
private function createReport(
|
||||||
|
ObjectManager $manager,
|
||||||
|
array $name,
|
||||||
|
array $options = array())
|
||||||
|
{
|
||||||
|
echo $name['fr']." \n";
|
||||||
|
|
||||||
|
$cFGroup = (new CustomFieldsGroup())
|
||||||
|
->setName($name)
|
||||||
|
->setEntity('Chill\ReportBundle\Entity\Report')
|
||||||
|
->setOptions($options);
|
||||||
|
|
||||||
|
$manager->persist($cFGroup);
|
||||||
|
|
||||||
|
return $cFGroup;
|
||||||
|
}
|
||||||
|
}
|
88
src/Bundle/ChillReport/DataFixtures/ORM/LoadReportACL.php
Normal file
88
src/Bundle/ChillReport/DataFixtures/ORM/LoadReportACL.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Julien Fastré <julien.fastre@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\ReportBundle\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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a role CHILL_REPORT_UPDATE & CHILL_REPORT_CREATE for all groups except administrative,
|
||||||
|
* and a role CHILL_REPORT_SEE for administrative
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class LoadReportACL extends AbstractFixture implements OrderedFixtureInterface
|
||||||
|
{
|
||||||
|
public function getOrder()
|
||||||
|
{
|
||||||
|
return 14999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 CHILL_REPORT_UPDATE & CHILL_REPORT_CREATE to %s "
|
||||||
|
. "permission group, scope '%s' \n",
|
||||||
|
$permissionsGroup->getName(), $scope->getName()['en']);
|
||||||
|
$roleScopeUpdate = (new RoleScope())
|
||||||
|
->setRole('CHILL_REPORT_UPDATE')
|
||||||
|
->setScope($scope);
|
||||||
|
$permissionsGroup->addRoleScope($roleScopeUpdate);
|
||||||
|
$roleScopeCreate = (new RoleScope())
|
||||||
|
->setRole('CHILL_REPORT_CREATE')
|
||||||
|
->setScope($scope);
|
||||||
|
$permissionsGroup->addRoleScope($roleScopeCreate);
|
||||||
|
$manager->persist($roleScopeUpdate);
|
||||||
|
$manager->persist($roleScopeCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
240
src/Bundle/ChillReport/DataFixtures/ORM/LoadReports.php
Normal file
240
src/Bundle/ChillReport/DataFixtures/ORM/LoadReports.php
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a suite of a modules, Chill is a software for social workers
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||||
|
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
|
||||||
|
use Faker\Factory as FakerFactory;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomField;
|
||||||
|
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load reports into DB
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class LoadReports extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
|
||||||
|
{
|
||||||
|
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Faker\Generator
|
||||||
|
*/
|
||||||
|
private $faker;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->faker = FakerFactory::create('fr_FR');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrder()
|
||||||
|
{
|
||||||
|
return 15002;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(ObjectManager $manager)
|
||||||
|
{
|
||||||
|
$this->createExpected($manager);
|
||||||
|
|
||||||
|
//create random 2 times, to allow multiple report on some people
|
||||||
|
$this->createRandom($manager, 90);
|
||||||
|
$this->createRandom($manager, 30);
|
||||||
|
|
||||||
|
$manager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createRandom(ObjectManager $manager, $percentage)
|
||||||
|
{
|
||||||
|
$people = $this->getPeopleRandom($percentage);
|
||||||
|
|
||||||
|
foreach ($people as $person) {
|
||||||
|
//create a report, set logement or education report
|
||||||
|
$report = (new Report())
|
||||||
|
->setPerson($person)
|
||||||
|
->setCFGroup(rand(0,10) > 5 ?
|
||||||
|
$this->getReference('cf_group_report_logement') :
|
||||||
|
$this->getReference('cf_group_report_education')
|
||||||
|
)
|
||||||
|
->setScope($this->getScopeRandom())
|
||||||
|
;
|
||||||
|
$this->fillReport($report);
|
||||||
|
$manager->persist($report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createExpected(ObjectManager $manager)
|
||||||
|
{
|
||||||
|
$charline = $this->container->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillPersonBundle:Person')
|
||||||
|
->findOneBy(array('lastName' => 'Charline'))
|
||||||
|
;
|
||||||
|
|
||||||
|
$report = (new Report())
|
||||||
|
->setPerson($charline)
|
||||||
|
->setCFGroup($this->getReference('cf_group_report_logement'))
|
||||||
|
->setDate(new \DateTime('2015-01-05'))
|
||||||
|
->setScope($this->getReference('scope_social'))
|
||||||
|
;
|
||||||
|
$this->fillReport($report);
|
||||||
|
|
||||||
|
$manager->persist($report);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return \Chill\MainBundle\Entity\Scope
|
||||||
|
*/
|
||||||
|
private function getScopeRandom()
|
||||||
|
{
|
||||||
|
$ref = LoadScopes::$references[array_rand(LoadScopes::$references)];
|
||||||
|
return $this->getReference($ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPeopleRandom($percentage)
|
||||||
|
{
|
||||||
|
$people = $this->container->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillPersonBundle:Person')
|
||||||
|
->findAll()
|
||||||
|
;
|
||||||
|
|
||||||
|
//keep only a part ($percentage) of the people
|
||||||
|
$selectedPeople = array();
|
||||||
|
foreach($people as $person) {
|
||||||
|
if (rand(0,100) < $percentage) {
|
||||||
|
$selectedPeople[] = $person;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $selectedPeople;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fillReport(Report $report)
|
||||||
|
{
|
||||||
|
//setUser
|
||||||
|
$usernameRef = array_rand(LoadUsers::$refs);
|
||||||
|
$report->setUser(
|
||||||
|
$this->getReference($usernameRef)
|
||||||
|
);
|
||||||
|
|
||||||
|
//set date if null
|
||||||
|
if ($report->getDate() === NULL) {
|
||||||
|
//set date. 30% of the dates are 2015-05-01
|
||||||
|
$expectedDate = new \DateTime('2015-01-05');
|
||||||
|
if (rand(0,100) < 30) {
|
||||||
|
$report->setDate($expectedDate);
|
||||||
|
} else {
|
||||||
|
$report->setDate($this->faker->dateTimeBetween('-1 year', 'now')
|
||||||
|
->setTime(0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fill data
|
||||||
|
$datas = array();
|
||||||
|
foreach ($report->getCFGroup()->getCustomFields() as $field) {
|
||||||
|
switch ($field->getType()) {
|
||||||
|
case 'title' :
|
||||||
|
$datas[$field->getSlug()] = array();
|
||||||
|
break;
|
||||||
|
case 'choice' :
|
||||||
|
$datas[$field->getSlug()] = $this->getRandomChoice($field);
|
||||||
|
break;
|
||||||
|
case 'text' :
|
||||||
|
$datas[$field->getSlug()] = $this->faker->realText($field->getOptions()['maxLength']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$report->setCFData($datas);
|
||||||
|
|
||||||
|
return $report;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pick a random choice
|
||||||
|
*
|
||||||
|
* @param CustomField $field
|
||||||
|
* @return string[]|string the array of slug if multiple, a single slug otherwise
|
||||||
|
*/
|
||||||
|
private function getRandomChoice(CustomField $field)
|
||||||
|
{
|
||||||
|
$choices = $field->getOptions()['choices'];
|
||||||
|
$multiple = $field->getOptions()['multiple'];
|
||||||
|
$other = $field->getOptions()['other'];
|
||||||
|
|
||||||
|
//add other if allowed
|
||||||
|
if($other) {
|
||||||
|
$choices[] = array('slug' => '_other');
|
||||||
|
}
|
||||||
|
|
||||||
|
//initialize results
|
||||||
|
$picked = array();
|
||||||
|
|
||||||
|
if ($multiple) {
|
||||||
|
$numberSelected = rand(1, count($choices) -1);
|
||||||
|
for ($i = 0; $i < $numberSelected; $i++) {
|
||||||
|
$picked[] = $this->pickChoice($choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($other) {
|
||||||
|
$result = array("_other" => NULL, "_choices" => $picked);
|
||||||
|
|
||||||
|
if (in_array('_other', $picked)) {
|
||||||
|
$result['_other'] = $this->faker->realText(70);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$picked = $this->pickChoice($choices);
|
||||||
|
|
||||||
|
if ($other) {
|
||||||
|
$result = array('_other' => NULL, '_choices' => $picked);
|
||||||
|
|
||||||
|
if ($picked === '_other') {
|
||||||
|
$result['_other'] = $this->faker->realText(70);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pick a choice within a 'choices' options (for choice type)
|
||||||
|
*
|
||||||
|
* @param array $choices
|
||||||
|
* @return the slug of the selected choice
|
||||||
|
*/
|
||||||
|
private function pickChoice(array $choices)
|
||||||
|
{
|
||||||
|
return $choices[array_rand($choices)]['slug'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\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\MainBundle\DependencyInjection\MissingBundleException;
|
||||||
|
use Chill\CustomFieldsBundle\Form\Type\LinkedCustomFieldsType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ChillReportExtension 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/fixtures.yaml');
|
||||||
|
$loader->load('services/export.yaml');
|
||||||
|
$loader->load('services/controller.yaml');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare the entity Report, as a customizable entity (can add custom fields)
|
||||||
|
*
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
public function declareReportAsCustomizable(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$bundles = $container->getParameter('kernel.bundles');
|
||||||
|
if (!isset($bundles['ChillCustomFieldsBundle'])) {
|
||||||
|
throw new MissingBundleException('ChillCustomFieldsBundle');
|
||||||
|
}
|
||||||
|
|
||||||
|
$container->prependExtensionConfig('chill_custom_fields',
|
||||||
|
array('customizables_entities' =>
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'class' => 'Chill\ReportBundle\Entity\Report',
|
||||||
|
'name' => 'ReportEntity',
|
||||||
|
'options' => array(
|
||||||
|
'summary_fields' => array(
|
||||||
|
'form_type' => LinkedCustomFieldsType::class,
|
||||||
|
'form_options' =>
|
||||||
|
[
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false
|
||||||
|
]
|
||||||
|
)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* declare routes from report bundle
|
||||||
|
*
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
private function declareRouting(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$container->prependExtensionConfig('chill_main', array(
|
||||||
|
'routing' => array(
|
||||||
|
'resources' => array(
|
||||||
|
'@ChillReportBundle/config/routes.yaml'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prependRoleHierarchy(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$container->prependExtensionConfig('security', array(
|
||||||
|
'role_hierarchy' => array(
|
||||||
|
'CHILL_REPORT_UPDATE' => array('CHILL_REPORT_SEE'),
|
||||||
|
'CHILL_REPORT_CREATE' => array('CHILL_REPORT_SEE')
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
public function prepend(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$this->declareReportAsCustomizable($container);
|
||||||
|
$this->declareRouting($container);
|
||||||
|
$this->prependRoleHierarchy($container);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/Bundle/ChillReport/DependencyInjection/Configuration.php
Normal file
29
src/Bundle/ChillReport/DependencyInjection/Configuration.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\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_report');
|
||||||
|
$rootNode = $treeBuilder->getRootNode('chill_report');
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
245
src/Bundle/ChillReport/Entity/Report.php
Normal file
245
src/Bundle/ChillReport/Entity/Report.php
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||||
|
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Report
|
||||||
|
*
|
||||||
|
* @package Chill\ReportBundle\Entity
|
||||||
|
* @ORM\Entity()
|
||||||
|
* @ORM\Table(name="report")
|
||||||
|
* @ORM\HasLifecycleCallbacks()
|
||||||
|
*/
|
||||||
|
class Report implements HasCenterInterface, HasScopeInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var integer
|
||||||
|
*
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\Column(name="id", type="integer")
|
||||||
|
* @ORM\GeneratedValue(strategy="AUTO")
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
|
||||||
|
*/
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Person
|
||||||
|
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\Person")
|
||||||
|
*/
|
||||||
|
private $person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
* @ORM\Column(type="datetime")
|
||||||
|
*/
|
||||||
|
private $date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Scope
|
||||||
|
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
|
||||||
|
*/
|
||||||
|
private $scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
* @ORM\Column(type="json_array")
|
||||||
|
*/
|
||||||
|
private $cFData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CustomFieldsGroup
|
||||||
|
* @ORM\ManyToOne(
|
||||||
|
* targetEntity="Chill\CustomFieldsBundle\Entity\CustomFieldsGroup")
|
||||||
|
*/
|
||||||
|
private $cFGroup;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get id
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set user
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function setUser(User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user
|
||||||
|
*
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public function getUser()
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set person
|
||||||
|
*
|
||||||
|
* @param Person $person
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function setPerson(Person $person)
|
||||||
|
{
|
||||||
|
$this->person = $person;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get person
|
||||||
|
*
|
||||||
|
* @return Person
|
||||||
|
*/
|
||||||
|
public function getPerson()
|
||||||
|
{
|
||||||
|
return $this->person;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set date
|
||||||
|
*
|
||||||
|
* @param \DateTime $date
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function setDate($date)
|
||||||
|
{
|
||||||
|
$this->date = $date;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get date
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getDate()
|
||||||
|
{
|
||||||
|
return $this->date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set scope
|
||||||
|
*
|
||||||
|
* @param string $scope
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function setScope(Scope $scope)
|
||||||
|
{
|
||||||
|
$this->scope = $scope;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get scope
|
||||||
|
*
|
||||||
|
* @return Scope
|
||||||
|
*/
|
||||||
|
public function getScope()
|
||||||
|
{
|
||||||
|
return $this->scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cFData
|
||||||
|
*
|
||||||
|
* @param array $cFData
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function setCFData(array $cFData)
|
||||||
|
{
|
||||||
|
$this->cFData = $cFData;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cFData
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCFData()
|
||||||
|
{
|
||||||
|
return $this->cFData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cFGroup
|
||||||
|
*
|
||||||
|
* @param CustomFieldsGroup $cFGroup
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function setCFGroup(CustomFieldsGroup $cFGroup)
|
||||||
|
{
|
||||||
|
$this->cFGroup = $cFGroup;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cFGroup
|
||||||
|
*
|
||||||
|
* @return CustomFieldsGroup
|
||||||
|
*/
|
||||||
|
public function getCFGroup()
|
||||||
|
{
|
||||||
|
return $this->cFGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Center
|
||||||
|
*/
|
||||||
|
public function getCenter()
|
||||||
|
{
|
||||||
|
return $this->person->getCenter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
556
src/Bundle/ChillReport/Export/Export/ReportList.php
Normal file
556
src/Bundle/ChillReport/Export/Export/ReportList.php
Normal file
@ -0,0 +1,556 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
namespace Chill\ReportBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Export\ListInterface;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
|
use Chill\ReportBundle\Security\Authorization\ReportVoter;
|
||||||
|
use Chill\PersonBundle\Export\Declarations;
|
||||||
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
use Symfony\Component\Validator\Constraints\Callback;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomField;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportList implements ListInterface, ExportElementValidatedInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CustomFieldsGroup
|
||||||
|
*/
|
||||||
|
protected $customfieldsGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var TranslatableStringHelper
|
||||||
|
*/
|
||||||
|
protected $translatableStringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
protected $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CustomFieldProvider
|
||||||
|
*/
|
||||||
|
protected $customFieldProvider;
|
||||||
|
|
||||||
|
protected $em;
|
||||||
|
|
||||||
|
protected $fields = array(
|
||||||
|
'person_id', 'person_firstName', 'person_lastName', 'person_birthdate',
|
||||||
|
'person_placeOfBirth', 'person_gender', 'person_memo', 'person_email', 'person_phonenumber',
|
||||||
|
'person_countryOfBirth', 'person_nationality', 'person_address_street_address_1',
|
||||||
|
'person_address_street_address_2', 'person_address_valid_from', 'person_address_postcode_label',
|
||||||
|
'person_address_postcode_code', 'person_address_country_name', 'person_address_country_code',
|
||||||
|
'report_id', 'report_user', 'report_date', 'report_scope'
|
||||||
|
);
|
||||||
|
|
||||||
|
protected $slugs = [];
|
||||||
|
|
||||||
|
function __construct(
|
||||||
|
CustomFieldsGroup $customfieldsGroup,
|
||||||
|
TranslatableStringHelper $translatableStringHelper,
|
||||||
|
TranslatorInterface $translator,
|
||||||
|
CustomFieldProvider $customFieldProvider,
|
||||||
|
EntityManagerInterface $em
|
||||||
|
) {
|
||||||
|
$this->customfieldsGroup = $customfieldsGroup;
|
||||||
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
|
$this->translator = $translator;
|
||||||
|
$this->customFieldProvider = $customFieldProvider;
|
||||||
|
$this->em = $em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$choices = array_combine($this->fields, $this->fields);
|
||||||
|
|
||||||
|
foreach ($this->getCustomFields() as $cf) {
|
||||||
|
$choices
|
||||||
|
[$this->translatableStringHelper->localize($cf->getName())]
|
||||||
|
=
|
||||||
|
$cf->getSlug();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a checkbox to select fields
|
||||||
|
$builder->add('fields', ChoiceType::class, array(
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => true,
|
||||||
|
'choices' => $choices,
|
||||||
|
'label' => 'Fields to include in export',
|
||||||
|
'choice_attr' => function($val, $key, $index) {
|
||||||
|
// add a 'data-display-target' for address fields
|
||||||
|
if (substr($val, 0, 8) === 'address_') {
|
||||||
|
return ['data-display-target' => 'address_date'];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'choice_label' => function($key, $label) {
|
||||||
|
switch (\substr($key, 0, 7)) {
|
||||||
|
case 'person_':
|
||||||
|
return $this->translator->trans(\substr($key, 7, \strlen($key) - 7)).
|
||||||
|
' ('.$this->translator->trans('Person').')';
|
||||||
|
case 'report_':
|
||||||
|
return $this->translator->trans(\ucfirst(\substr($key, 7, \strlen($key) - 7))).
|
||||||
|
' ('.$this->translator->trans('Report').')';
|
||||||
|
default:
|
||||||
|
return $label.
|
||||||
|
' ('.$this->translator->trans("Report's question").')';;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'constraints' => [new Callback(array(
|
||||||
|
'callback' => function($selected, ExecutionContextInterface $context) {
|
||||||
|
if (count($selected) === 0) {
|
||||||
|
$context->buildViolation('You must select at least one element')
|
||||||
|
->atPath('fields')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))]
|
||||||
|
));
|
||||||
|
|
||||||
|
// add a date field for addresses
|
||||||
|
$builder->add('address_date', ChillDateType::class, array(
|
||||||
|
'label' => "Address valid at this date",
|
||||||
|
'data' => new \DateTime(),
|
||||||
|
'required' => false,
|
||||||
|
'block_name' => 'list_export_form_address_date'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
// get the field starting with address_
|
||||||
|
$addressFields = array_filter(function($el) {
|
||||||
|
return substr($el, 0, 8) === 'address_';
|
||||||
|
}, $this->fields);
|
||||||
|
|
||||||
|
// check if there is one field starting with address in data
|
||||||
|
if (count(array_intersect($data['fields'], $addressFields)) > 0) {
|
||||||
|
// if a field address is checked, the date must not be empty
|
||||||
|
if (empty($data['address_date'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation("You must set this date if an address is checked")
|
||||||
|
->atPath('address_date')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get custom fields associated with person
|
||||||
|
*
|
||||||
|
* @return CustomField[]
|
||||||
|
*/
|
||||||
|
private function getCustomFields()
|
||||||
|
{
|
||||||
|
return \array_filter($this->customfieldsGroup
|
||||||
|
->getCustomFields()->toArray(), function(CustomField $cf) {
|
||||||
|
return $cf->getType() !== 'title';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return array(FormatterInterface::TYPE_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return $this->translator->trans(
|
||||||
|
"Generate list of report '%type%'",
|
||||||
|
[
|
||||||
|
'%type%' => $this->translatableStringHelper->localize($this->customfieldsGroup->getName())
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @param type $key
|
||||||
|
* @param array $values
|
||||||
|
* @param type $data
|
||||||
|
* @return type
|
||||||
|
*/
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'person_birthdate':
|
||||||
|
case 'report_date':
|
||||||
|
// for birthdate or report date, we have to transform the string into a date
|
||||||
|
// to format the date correctly.
|
||||||
|
return function($value) use ($key) {
|
||||||
|
if ($value === '_header') {
|
||||||
|
return $key === 'person_birthdate' ? 'birthdate' : 'report_date';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($value))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key === 'person_birthdate') {
|
||||||
|
$date = \DateTime::createFromFormat('Y-m-d', $value);
|
||||||
|
} else {
|
||||||
|
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||||
|
}
|
||||||
|
// check that the creation could occurs.
|
||||||
|
if ($date === false) {
|
||||||
|
throw new \Exception(sprintf("The value %s could "
|
||||||
|
. "not be converted to %s", $value, \DateTime::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $date->format('d-m-Y');
|
||||||
|
};
|
||||||
|
case 'report_scope':
|
||||||
|
$qb = $this->em->getRepository(Scope::class)
|
||||||
|
->createQueryBuilder('s');
|
||||||
|
$qb->addSelect('s.name')
|
||||||
|
->addSelect('s.id')
|
||||||
|
->where($qb->expr()->in('s.id', $values))
|
||||||
|
;
|
||||||
|
$rows = $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
foreach($rows as $row) {
|
||||||
|
$scopes[$row['id']] = $this->translatableStringHelper
|
||||||
|
->localize($row['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function($value) use ($scopes) {
|
||||||
|
if ($value === '_header') {
|
||||||
|
return 'circle';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $scopes[$value];
|
||||||
|
};
|
||||||
|
case 'report_user':
|
||||||
|
$qb = $this->em->getRepository(User::class)
|
||||||
|
->createQueryBuilder('u');
|
||||||
|
$qb->addSelect('u.username')
|
||||||
|
->addSelect('u.id')
|
||||||
|
->where($qb->expr()->in('u.id', $values))
|
||||||
|
;
|
||||||
|
$rows = $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
foreach($rows as $row) {
|
||||||
|
$users[$row['id']] = $row['username'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return function($value) use ($users) {
|
||||||
|
if ($value === '_header') {
|
||||||
|
return 'user';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $users[$value];
|
||||||
|
};
|
||||||
|
case 'person_gender' :
|
||||||
|
// for gender, we have to translate men/women statement
|
||||||
|
return function($value) {
|
||||||
|
if ($value === '_header') { return 'gender'; }
|
||||||
|
|
||||||
|
return $this->translator->trans($value);
|
||||||
|
};
|
||||||
|
case 'person_countryOfBirth':
|
||||||
|
case 'person_nationality':
|
||||||
|
$countryRepository = $this->em
|
||||||
|
->getRepository('ChillMainBundle:Country');
|
||||||
|
|
||||||
|
// load all countries in a single query
|
||||||
|
$countryRepository->findBy(array('countryCode' => $values));
|
||||||
|
|
||||||
|
return function($value) use ($key, $countryRepository) {
|
||||||
|
if ($value === '_header') { return \strtolower($key); }
|
||||||
|
|
||||||
|
if ($value === NULL) {
|
||||||
|
return $this->translator->trans('no data');
|
||||||
|
}
|
||||||
|
|
||||||
|
$country = $countryRepository->find($value);
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize(
|
||||||
|
$country->getName());
|
||||||
|
};
|
||||||
|
case 'person_address_country_name':
|
||||||
|
return function($value) use ($key) {
|
||||||
|
if ($value === '_header') { return \strtolower($key); }
|
||||||
|
|
||||||
|
if ($value === NULL) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize(json_decode($value, true));
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
// for fields which are associated with person
|
||||||
|
if (in_array($key, $this->fields)) {
|
||||||
|
return function($value) use ($key) {
|
||||||
|
if ($value === '_header') { return \strtolower($key); }
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return $this->getLabelForCustomField($key, $values, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getLabelForCustomField($key, array $values, $data)
|
||||||
|
{
|
||||||
|
// for fields which are custom fields
|
||||||
|
/* @var $cf CustomField */
|
||||||
|
$cf = $this->em
|
||||||
|
->getRepository(CustomField::class)
|
||||||
|
->findOneBy(array('slug' => $this->DQLToSlug($key)));
|
||||||
|
|
||||||
|
$cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType());
|
||||||
|
$defaultFunction = function($value) use ($cf) {
|
||||||
|
if ($value === '_header') {
|
||||||
|
return $this->translatableStringHelper->localize($cf->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->customFieldProvider
|
||||||
|
->getCustomFieldByType($cf->getType())
|
||||||
|
->render(json_decode($value, true), $cf, 'csv');
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($cfType instanceof CustomFieldChoice and $cfType->isMultiple($cf)) {
|
||||||
|
return function($value) use ($cf, $cfType, $key) {
|
||||||
|
$slugChoice = $this->extractInfosFromSlug($key)['additionnalInfos']['choiceSlug'];
|
||||||
|
$decoded = \json_decode($value, true);
|
||||||
|
|
||||||
|
if ($value === '_header') {
|
||||||
|
|
||||||
|
$label = $cfType->getChoices($cf)[$slugChoice];
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($cf->getName())
|
||||||
|
.' | '.$label;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($slugChoice === '_other' and $cfType->isChecked($cf, $choiceSlug, $decoded)) {
|
||||||
|
return $cfType->extractOtherValue($cf, $decoded);
|
||||||
|
} else {
|
||||||
|
return $cfType->isChecked($cf, $slugChoice, $decoded);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return $defaultFunction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
$fields = array();
|
||||||
|
|
||||||
|
foreach ($data['fields'] as $key) {
|
||||||
|
if (in_array($key, $this->fields)) {
|
||||||
|
$fields[] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the key from slugs and return
|
||||||
|
return \array_merge($fields, \array_keys($this->slugs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clean a slug to be usable by DQL
|
||||||
|
*
|
||||||
|
* @param string $slugsanitize
|
||||||
|
* @param string $type the type of the customfield, if required (currently only for choices)
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function slugToDQL($slug, $type = "default", array $additionalInfos = [])
|
||||||
|
{
|
||||||
|
$uid = 'slug_'.\uniqid();
|
||||||
|
|
||||||
|
$this->slugs[$uid] = [
|
||||||
|
'slug' => $slug,
|
||||||
|
'type' => $type,
|
||||||
|
'additionnalInfos' => $additionalInfos
|
||||||
|
];
|
||||||
|
|
||||||
|
return $uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function DQLToSlug($cleanedSlug)
|
||||||
|
{
|
||||||
|
return $this->slugs[$cleanedSlug]['slug'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type $cleanedSlug
|
||||||
|
* @return an array with keys = 'slug', 'type', 'additionnalInfo'
|
||||||
|
*/
|
||||||
|
private function extractInfosFromSlug($slug)
|
||||||
|
{
|
||||||
|
return $this->slugs[$slug];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return $this->translator->trans(
|
||||||
|
"List for report '%type%'",
|
||||||
|
[
|
||||||
|
'%type%' => $this->translatableStringHelper->localize($this->customfieldsGroup->getName())
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return 'report';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
|
||||||
|
{
|
||||||
|
$centers = array_map(function($el) { return $el['center']; }, $acl);
|
||||||
|
|
||||||
|
// throw an error if any fields are present
|
||||||
|
if (!\array_key_exists('fields', $data)) {
|
||||||
|
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields "
|
||||||
|
. "have been checked");
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb = $this->em->createQueryBuilder();
|
||||||
|
|
||||||
|
// process fields which are not custom fields
|
||||||
|
foreach ($this->fields as $f) {
|
||||||
|
// do not add fields which are not selected
|
||||||
|
if (!\in_array($f, $data['fields'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a column to the query for each field
|
||||||
|
switch ($f) {
|
||||||
|
case 'person_countryOfBirth':
|
||||||
|
case 'person_nationality':
|
||||||
|
$suffix = \substr($f, 7);
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $suffix, $f));
|
||||||
|
break;
|
||||||
|
case 'person_address_street_address_1':
|
||||||
|
case 'person_address_street_address_2':
|
||||||
|
case 'person_address_valid_from':
|
||||||
|
case 'person_address_postcode_label':
|
||||||
|
case 'person_address_postcode_code':
|
||||||
|
case 'person_address_country_name':
|
||||||
|
case 'person_address_country_code':
|
||||||
|
// remove 'person_'
|
||||||
|
$suffix = \substr($f, 7);
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf(
|
||||||
|
'GET_PERSON_ADDRESS_%s(person.id, :address_date) AS %s',
|
||||||
|
// get the part after address_
|
||||||
|
strtoupper(substr($suffix, 8)),
|
||||||
|
$f));
|
||||||
|
$qb->setParameter('address_date', $data['address_date']);
|
||||||
|
break;
|
||||||
|
case 'report_scope':
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(report.scope) AS %s', 'report_scope'));
|
||||||
|
break;
|
||||||
|
case 'report_user':
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(report.user) AS %s', 'report_user'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$prefix = \substr($f, 0, 7);
|
||||||
|
$suffix = \substr($f, 7);
|
||||||
|
|
||||||
|
switch($prefix) {
|
||||||
|
case 'person_':
|
||||||
|
$qb->addSelect(sprintf('person.%s as %s', $suffix, $f));
|
||||||
|
break;
|
||||||
|
case 'report_':
|
||||||
|
$qb->addSelect(sprintf('report.%s as %s', $suffix, $f));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \LogicException("this prefix $prefix should "
|
||||||
|
. "not be encountered. Full field: $f");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// process fields which are custom fields
|
||||||
|
foreach ($this->getCustomFields() as $cf) {
|
||||||
|
// do not add custom fields which are not selected
|
||||||
|
if (!\in_array($cf->getSlug(), $data['fields'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType());
|
||||||
|
|
||||||
|
// if is multiple, split into multiple columns
|
||||||
|
if ($cfType instanceof CustomFieldChoice and $cfType->isMultiple($cf)) {
|
||||||
|
foreach($cfType->getChoices($cf) as $choiceSlug => $label) {
|
||||||
|
$slug = $this->slugToDQL($cf->getSlug(), 'choice', [ 'choiceSlug' => $choiceSlug ]);
|
||||||
|
$qb->addSelect(
|
||||||
|
sprintf('GET_JSON_FIELD_BY_KEY(report.cFData, :slug%s) AS %s',
|
||||||
|
$slug, $slug));
|
||||||
|
$qb->setParameter(sprintf('slug%s', $slug), $cf->getSlug());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// not multiple, add a single column
|
||||||
|
$slug = $this->slugToDQL($cf->getSlug());
|
||||||
|
$qb->addSelect(
|
||||||
|
sprintf('GET_JSON_FIELD_BY_KEY(report.cFData, :slug%s) AS %s',
|
||||||
|
$slug, $slug));
|
||||||
|
$qb->setParameter(sprintf('slug%s', $slug), $cf->getSlug());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb
|
||||||
|
->from(Report::class, 'report')
|
||||||
|
->leftJoin('report.person', 'person')
|
||||||
|
->join('person.center', 'center')
|
||||||
|
->andWhere($qb->expr()->eq('report.cFGroup', ':cFGroup'))
|
||||||
|
->setParameter('cFGroup', $this->customfieldsGroup)
|
||||||
|
->andWhere('center IN (:authorized_centers)')
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
;
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole()
|
||||||
|
{
|
||||||
|
return new Role(ReportVoter::LISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [Declarations::PERSON_IMPLIED_IN, Declarations::PERSON_TYPE, 'report'];
|
||||||
|
}
|
||||||
|
}
|
77
src/Bundle/ChillReport/Export/Export/ReportListProvider.php
Normal file
77
src/Bundle/ChillReport/Export/Export/ReportListProvider.php
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
namespace Chill\ReportBundle\Export\Export;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Chill\MainBundle\Export\ExportElementsProviderInterface;
|
||||||
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportListProvider implements ExportElementsProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var EntityManagerInterface
|
||||||
|
*/
|
||||||
|
protected $em;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var TranslatableStringHelper
|
||||||
|
*/
|
||||||
|
protected $translatableStringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CustomFieldProvider
|
||||||
|
*/
|
||||||
|
protected $customFieldProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
protected $translator;
|
||||||
|
|
||||||
|
function __construct(
|
||||||
|
EntityManagerInterface $em,
|
||||||
|
TranslatableStringHelper $translatableStringHelper,
|
||||||
|
TranslatorInterface $translator,
|
||||||
|
CustomFieldProvider $customFieldProvider
|
||||||
|
) {
|
||||||
|
$this->em = $em;
|
||||||
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
|
$this->translator = $translator;
|
||||||
|
$this->customFieldProvider = $customFieldProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function getExportElements()
|
||||||
|
{
|
||||||
|
$groups = $this->em->getRepository(CustomFieldsGroup::class)
|
||||||
|
->findBy([ 'entity' => Report::class ])
|
||||||
|
;
|
||||||
|
$reports = [];
|
||||||
|
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$reports[$group->getId()] = new ReportList(
|
||||||
|
$group,
|
||||||
|
$this->translatableStringHelper,
|
||||||
|
$this->translator,
|
||||||
|
$this->customFieldProvider,
|
||||||
|
$this->em);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reports;
|
||||||
|
}
|
||||||
|
}
|
75
src/Bundle/ChillReport/Export/Filter/ReportDateFilter.php
Normal file
75
src/Bundle/ChillReport/Export/Filter/ReportDateFilter.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
namespace Chill\ReportBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Doctrine\ORM\Query\Expr;
|
||||||
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportDateFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
public function addRole()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$where = $qb->getDQLPart('where');
|
||||||
|
$clause = $qb->expr()->between('report.date', ':report_date_filter_date_from',
|
||||||
|
':report_date_filter_date_to');
|
||||||
|
|
||||||
|
if ($where instanceof Expr\Andx) {
|
||||||
|
$where->add($clause);
|
||||||
|
} else {
|
||||||
|
$where = $qb->expr()->andX($clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->add('where', $where);
|
||||||
|
$qb->setParameter('report_date_filter_date_from', $data['date_from']);
|
||||||
|
$qb->setParameter('report_date_filter_date_to', $data['date_to']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn()
|
||||||
|
{
|
||||||
|
return 'report';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('date_from', ChillDateType::class, array(
|
||||||
|
'label' => "Report is after this date",
|
||||||
|
'data' => new \DateTime(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$builder->add('date_to', ChillDateType::class, array(
|
||||||
|
'label' => "Report is before this date",
|
||||||
|
'data' => new \DateTime(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
return array('Filtered by report\'s date: '
|
||||||
|
. 'between %date_from% and %date_to%', array(
|
||||||
|
'%date_from%' => $data['date_from']->format('d-m-Y'),
|
||||||
|
'%date_to%' => $data['date_to']->format('d-m-Y')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filter by report\'s date';
|
||||||
|
}
|
||||||
|
}
|
120
src/Bundle/ChillReport/Form/ReportType.php
Normal file
120
src/Bundle/ChillReport/Form/ReportType.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Chill\MainBundle\Form\Type\AppendScopeChoiceTypeTrait;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
|
||||||
|
|
||||||
|
class ReportType extends AbstractType
|
||||||
|
{
|
||||||
|
use AppendScopeChoiceTypeTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var AuthorizationHelper
|
||||||
|
*/
|
||||||
|
protected $authorizationHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var TranslatableStringHelper
|
||||||
|
*/
|
||||||
|
protected $translatableStringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Doctrine\Persistence\ObjectManager
|
||||||
|
*/
|
||||||
|
protected $om;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Chill\MainBundle\Entity\User
|
||||||
|
*/
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
public function __construct(AuthorizationHelper $helper,
|
||||||
|
TokenStorageInterface $tokenStorage,
|
||||||
|
TranslatableStringHelper $translatableStringHelper,
|
||||||
|
ObjectManager $om)
|
||||||
|
{
|
||||||
|
$this->authorizationHelper = $helper;
|
||||||
|
$this->user = $tokenStorage->getToken()->getUser();
|
||||||
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
|
$this->om = $om;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param FormBuilderInterface $builder
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('user')
|
||||||
|
->add('date', DateType::class,
|
||||||
|
array('required' => true, 'widget' => 'single_text', 'format' => 'dd-MM-yyyy'))
|
||||||
|
->add('cFData', CustomFieldType::class,
|
||||||
|
array('attr' => array('class' => 'cf-fields'),
|
||||||
|
'group' => $options['cFGroup']))
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->appendScopeChoices($builder, $options['role'], $options['center'],
|
||||||
|
$this->user, $this->authorizationHelper,
|
||||||
|
$this->translatableStringHelper,
|
||||||
|
$this->om);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param OptionsResolver $resolver
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults(array(
|
||||||
|
'data_class' => 'Chill\ReportBundle\Entity\Report'
|
||||||
|
));
|
||||||
|
|
||||||
|
$resolver->setRequired(array(
|
||||||
|
'cFGroup',
|
||||||
|
));
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('cFGroup', 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup');
|
||||||
|
|
||||||
|
$this->appendScopeChoicesOptions($resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getBlockPrefix()
|
||||||
|
{
|
||||||
|
return 'chill_reportbundle_report';
|
||||||
|
}
|
||||||
|
}
|
661
src/Bundle/ChillReport/LICENSE
Normal file
661
src/Bundle/ChillReport/LICENSE
Normal file
@ -0,0 +1,661 @@
|
|||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
9
src/Bundle/ChillReport/README.md
Normal file
9
src/Bundle/ChillReport/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
ChillReport
|
||||||
|
===========
|
||||||
|
|
||||||
|
The bundle for reports. This is a bundle of the Chill project.
|
||||||
|
|
||||||
|
Documentation & installation
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Read documentation here : http://chill.readthedocs.org
|
BIN
src/Bundle/ChillReport/Resources/test/Fixtures/.DS_Store
vendored
Normal file
BIN
src/Bundle/ChillReport/Resources/test/Fixtures/.DS_Store
vendored
Normal file
Binary file not shown.
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Kernel;
|
||||||
|
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||||
|
|
||||||
|
class AppKernel extends Kernel
|
||||||
|
{
|
||||||
|
public function registerBundles()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
|
||||||
|
new Chill\ReportBundle\ChillReportBundle(),
|
||||||
|
new Symfony\Bundle\MonologBundle\MonologBundle(),
|
||||||
|
new Symfony\Bundle\TwigBundle\TwigBundle(),
|
||||||
|
new Chill\CustomFieldsBundle\ChillCustomFieldsBundle(),
|
||||||
|
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
|
||||||
|
new Chill\MainBundle\ChillMainBundle(),
|
||||||
|
new Chill\PersonBundle\ChillPersonBundle(),
|
||||||
|
new \Symfony\Bundle\AsseticBundle\AsseticBundle(),
|
||||||
|
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
|
||||||
|
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
|
||||||
|
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerContainerConfiguration(LoaderInterface $loader)
|
||||||
|
{
|
||||||
|
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCacheDir()
|
||||||
|
{
|
||||||
|
return sys_get_temp_dir().'/ChillReportBundle/cache';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLogDir()
|
||||||
|
{
|
||||||
|
return sys_get_temp_dir().'/ChillReportBundle/logs';
|
||||||
|
}
|
||||||
|
}
|
2
src/Bundle/ChillReport/Resources/test/Fixtures/App/app/DoctrineMigrations/.gitignore
vendored
Normal file
2
src/Bundle/ChillReport/Resources/test/Fixtures/App/app/DoctrineMigrations/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
|
||||||
|
/** @var ClassLoader $loader */
|
||||||
|
$loader = require __DIR__.'/../../../../../vendor/autoload.php';
|
||||||
|
|
||||||
|
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
|
||||||
|
|
||||||
|
return $loader;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,73 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: parameters.yml }
|
||||||
|
|
||||||
|
framework:
|
||||||
|
secret: Not very secret
|
||||||
|
router: { resource: "%kernel.root_dir%/config/routing.yml" }
|
||||||
|
form: true
|
||||||
|
csrf_protection: true
|
||||||
|
session: ~
|
||||||
|
default_locale: fr
|
||||||
|
translator: { fallback: fr }
|
||||||
|
profiler: { only_exceptions: false }
|
||||||
|
templating: #required for assetic. Remove if not needed
|
||||||
|
engines: ['twig']
|
||||||
|
|
||||||
|
doctrine:
|
||||||
|
dbal:
|
||||||
|
driver: pdo_pgsql
|
||||||
|
host: "%database_host%"
|
||||||
|
port: "%database_port%"
|
||||||
|
dbname: "%database_name%"
|
||||||
|
user: "%database_user%"
|
||||||
|
password: "%database_password%"
|
||||||
|
charset: UTF8
|
||||||
|
orm:
|
||||||
|
auto_generate_proxy_classes: "%kernel.debug%"
|
||||||
|
auto_mapping: true
|
||||||
|
|
||||||
|
# Assetic Configuration
|
||||||
|
assetic:
|
||||||
|
debug: "%kernel.debug%"
|
||||||
|
use_controller: false
|
||||||
|
bundles: [ ]
|
||||||
|
#java: /usr/bin/java
|
||||||
|
filters:
|
||||||
|
cssrewrite: ~
|
||||||
|
|
||||||
|
chill_main:
|
||||||
|
available_languages: [ fr, nl, en ]
|
||||||
|
|
||||||
|
security:
|
||||||
|
providers:
|
||||||
|
users:
|
||||||
|
entity:
|
||||||
|
class: Chill\MainBundle\Entity\User
|
||||||
|
property: username
|
||||||
|
|
||||||
|
encoders:
|
||||||
|
Chill\MainBundle\Entity\User:
|
||||||
|
algorithm: bcrypt
|
||||||
|
|
||||||
|
firewalls:
|
||||||
|
dev:
|
||||||
|
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||||
|
security: false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
anonymous: ~
|
||||||
|
http_basic: ~
|
||||||
|
form_login:
|
||||||
|
csrf_parameter: _csrf_token
|
||||||
|
csrf_token_id: authenticate
|
||||||
|
csrf_provider: form.csrf_provider
|
||||||
|
|
||||||
|
logout: ~
|
||||||
|
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||||
|
- { path: ^/admin, roles: ROLE_ADMIN }
|
||||||
|
- { path: ^/, roles: ROLE_USER }
|
@ -0,0 +1,7 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: config.yml } #here we import a config.yml file, this is not required
|
||||||
|
|
||||||
|
framework:
|
||||||
|
test: ~
|
||||||
|
session:
|
||||||
|
storage_id: session.storage.filesystem
|
@ -0,0 +1,7 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: config.yml } #here we import a config.yml file, this is not required
|
||||||
|
|
||||||
|
framework:
|
||||||
|
test: ~
|
||||||
|
session:
|
||||||
|
storage_id: session.storage.filesystem
|
@ -0,0 +1,7 @@
|
|||||||
|
parameters:
|
||||||
|
database_host: chill__database
|
||||||
|
database_port: 5432
|
||||||
|
database_name: postgres
|
||||||
|
database_user: postgres
|
||||||
|
database_password: postgres
|
||||||
|
locale: fr
|
@ -0,0 +1,13 @@
|
|||||||
|
parameters:
|
||||||
|
database_host: localhost
|
||||||
|
# database_host: 127.0.0.1
|
||||||
|
database_port: 5435
|
||||||
|
# database_port: 5454
|
||||||
|
database_name: postgres
|
||||||
|
database_user: postgres
|
||||||
|
database_password: postgres
|
||||||
|
locale: fr
|
||||||
|
secret: ThisTokenIsNotSoSecretChangeIt
|
||||||
|
debug_toolbar: true
|
||||||
|
debug_redirects: false
|
||||||
|
use_assetic_controller: true
|
@ -0,0 +1,7 @@
|
|||||||
|
parameters:
|
||||||
|
database_host: 127.0.0.1
|
||||||
|
database_port: 5432
|
||||||
|
database_name: symfony
|
||||||
|
database_user: symfony
|
||||||
|
database_password: symfony
|
||||||
|
locale: fr
|
@ -0,0 +1,3 @@
|
|||||||
|
chill_routes:
|
||||||
|
resource: .
|
||||||
|
type: chill_routes
|
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
|
use Symfony\Component\Console\Input\ArgvInput;
|
||||||
|
use Symfony\Component\Debug\Debug;
|
||||||
|
|
||||||
|
// if you don't want to setup permissions the proper way, just uncomment the following PHP line
|
||||||
|
// read https://symfony.com/doc/current/setup.html#checking-symfony-application-configuration-and-setup
|
||||||
|
// for more information
|
||||||
|
//umask(0000);
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
require __DIR__.'/autoload.php';
|
||||||
|
|
||||||
|
$input = new ArgvInput();
|
||||||
|
$env = $input->getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev');
|
||||||
|
$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod';
|
||||||
|
|
||||||
|
if ($debug) {
|
||||||
|
Debug::enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
$kernel = new AppKernel($env, $debug);
|
||||||
|
$application = new Application($kernel);
|
||||||
|
$application->run($input);
|
2
src/Bundle/ChillReport/Resources/test/Fixtures/App/web/.gitignore
vendored
Normal file
2
src/Bundle/ChillReport/Resources/test/Fixtures/App/web/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
47
src/Bundle/ChillReport/Resources/views/Report/edit.html.twig
Normal file
47
src/Bundle/ChillReport/Resources/views/Report/edit.html.twig
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{#
|
||||||
|
* 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 = 'report_select_type' %}
|
||||||
|
|
||||||
|
{% block title %}{{ 'Report edit' |trans }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block personcontent %}
|
||||||
|
{{ form_start(edit_form) }}
|
||||||
|
|
||||||
|
{{ form_row(edit_form.user) }}
|
||||||
|
{{ form_row(edit_form.date) }}
|
||||||
|
{{ form_row(edit_form.scope) }}
|
||||||
|
{{ form_row(edit_form.cFData) }}
|
||||||
|
|
||||||
|
{{ form_widget(edit_form) }}
|
||||||
|
<ul class="sticky-form-buttons record_actions">
|
||||||
|
<li class="cancel">
|
||||||
|
<a href="{{ path('report_list', { 'person_id': person.id } ) }}" class="sc-button bt-cancel">{{ 'Back to the list'|trans }}</a>
|
||||||
|
<li>
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-save" type="submit">{{ 'Save report'|trans }}</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{{ form_end(edit_form) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
chill.displayAlertWhenLeavingModifiedForm('form[name="{{ edit_form.vars.form.vars.name }}"]', '{{ "You are going to leave a page with unsubmitted data. Are you sure you want to leave ?"|trans }}');
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,29 @@
|
|||||||
|
"{{ 'Report id'|trans }}",{#
|
||||||
|
#}"{{ 'Person'|trans }}",{#
|
||||||
|
#}"{{ 'Person id'|trans }}",{#
|
||||||
|
#}"{{ 'Date'|trans }}",{#
|
||||||
|
#}"{{ 'User'|trans }}",{#
|
||||||
|
#}"{{ 'Report type'|trans }}",{#
|
||||||
|
#}{% for customField in cf_group.activeCustomFields %}{#
|
||||||
|
#}"{{ chill_custom_field_label(customField) }}"{% if not loop.last %},{% endif %}{#
|
||||||
|
#}{% endfor %}{#
|
||||||
|
|
||||||
|
#}{{ '\r\n'|raw }}{#
|
||||||
|
#}{% for report in reports %}{#
|
||||||
|
#}{{ report.id }},{#
|
||||||
|
#}"{{ report.person|csv_cell }}",{#
|
||||||
|
#}"{{ report.person.id|csv_cell }}",{#
|
||||||
|
#}"{{ report.date|format_date('short', 'none') }}",{#
|
||||||
|
#}"{{ report.user|csv_cell }}",{#
|
||||||
|
#}"{{ report.cFGroup.getName(app.request.locale)|csv_cell }}",{#
|
||||||
|
#}{% for customField in report.cFGroup.activeCustomFields %}{#
|
||||||
|
#}{% if customField.type == 'title' %}{#
|
||||||
|
#}""{#
|
||||||
|
#}{% else %}{#
|
||||||
|
#}"{{ chill_custom_field_widget(report.cFData , customField, 'csv') }}"{#
|
||||||
|
#}{% endif %}{#
|
||||||
|
#}{% if not loop.last %},{% endif %}{#
|
||||||
|
#}{% endfor %}{#
|
||||||
|
|
||||||
|
#}{{ '\r\n'|raw }}{#
|
||||||
|
#}{% endfor %}
|
80
src/Bundle/ChillReport/Resources/views/Report/list.html.twig
Normal file
80
src/Bundle/ChillReport/Resources/views/Report/list.html.twig
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{#
|
||||||
|
* 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 = 'report_select_type' %}
|
||||||
|
|
||||||
|
{% block title %}{{ 'Report list' |trans }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block personcontent %}
|
||||||
|
|
||||||
|
<h1>{{ 'Report list'|trans }}</h1>
|
||||||
|
|
||||||
|
{% if reports|length == 0 %}
|
||||||
|
<p>
|
||||||
|
<span class="chill-no-data-statement">{{ "No report registered for this person."|trans }}</span>
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<table class="" style="width:100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="chill-red">{{ 'Date' | trans }}</th>
|
||||||
|
<th class="chill-green">{{ 'Report type' | trans }}</th>
|
||||||
|
<th class="chill-orange">{{ 'Circle' | trans }}</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for report in reports %}
|
||||||
|
<tr>
|
||||||
|
<td>{% if report.date %}{{ report.date|format_date('long') }}{% endif %}</td>
|
||||||
|
<td>{{ report.cFGroup.getName|localize_translatable_string }}</td>
|
||||||
|
<td>{{ report.scope.name|localize_translatable_string }}</td>
|
||||||
|
<td>
|
||||||
|
<ul class="record_actions">
|
||||||
|
{% if is_granted('CHILL_REPORT_SEE', report) %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('report_view', { 'person_id': report.person.id, 'report_id': report.id }) }}" class="sc-button bt-view"></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_granted('CHILL_REPORT_UPDATE', report) %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('report_edit', { 'person_id': report.person.id, 'report_id': report.id }) }}" class="sc-button bt-update"></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if reports|length < paginator.getTotalItems %}
|
||||||
|
{{ chill_pagination(paginator) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('report_select_type', { 'person_id' : person.id } ) }}" class="sc-button bt-new">
|
||||||
|
{{ 'Create a new report'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
45
src/Bundle/ChillReport/Resources/views/Report/new.html.twig
Normal file
45
src/Bundle/ChillReport/Resources/views/Report/new.html.twig
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{#
|
||||||
|
* 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 = 'report_select_type' %}
|
||||||
|
|
||||||
|
{% block title %}{{ 'Add a report'|trans }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block personcontent %}
|
||||||
|
{{ form_start(form) }}
|
||||||
|
|
||||||
|
{{ form_row(form.user) }}
|
||||||
|
{{ form_row(form.date) }}
|
||||||
|
{{ form_row(form.scope) }}
|
||||||
|
{{ form_row(form.cFData) }}
|
||||||
|
<ul class="sticky-form-buttons record_actions">
|
||||||
|
<li class="cancel">
|
||||||
|
<a href="{{ path('report_list', { 'person_id': person.id } ) }}" class="sc-button bt-cancel">{{ 'Back to the list'|trans }}</a>
|
||||||
|
<li>
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-create" type="submit"> {{ 'Add report'|trans }}</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
chill.displayAlertWhenLeavingUnsubmittedForm('form[name="{{ form.vars.form.vars.name }}"]', '{{ "You are going to leave a page with unsubmitted data. Are you sure you want to leave ?"|trans }}');
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,40 @@
|
|||||||
|
{#
|
||||||
|
* 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 = 'report_select_type' %}
|
||||||
|
|
||||||
|
{% block title %}{{ 'Add a report'|trans() }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block personcontent %}
|
||||||
|
|
||||||
|
{{ form_start(form) }}
|
||||||
|
{{ form_widget(form.cFGroup) }}
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li class="cancel">
|
||||||
|
<a href="{{ path('report_list', { 'person_id': person.id } ) }}" class="sc-button bt-cancel">{{ 'Back to the list'|trans }}</a>
|
||||||
|
<li>
|
||||||
|
<button class="sc-button green" type="submit">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
{{ 'Create a new report'|trans }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -0,0 +1,33 @@
|
|||||||
|
{#
|
||||||
|
* 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 "@ChillMain/Export/layout.html.twig" %}
|
||||||
|
|
||||||
|
{% set activeRouteKey = 'report_export_list' %}
|
||||||
|
|
||||||
|
{% block title %}{{ 'Reports export'|trans() }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block export_content %}
|
||||||
|
|
||||||
|
{{ form_start(form) }}
|
||||||
|
{{ form_widget(form.cFGroup) }}
|
||||||
|
<button class="sc-button green" type="submit">
|
||||||
|
<i class="fa fa-upload"></i>
|
||||||
|
{{ 'Export this kind of reports'|trans }}
|
||||||
|
</button>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
57
src/Bundle/ChillReport/Resources/views/Report/view.html.twig
Normal file
57
src/Bundle/ChillReport/Resources/views/Report/view.html.twig
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{#
|
||||||
|
* 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 = 'report_select_type' %}
|
||||||
|
|
||||||
|
{% block title %}{{ 'Report view' |trans() }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block personcontent %}
|
||||||
|
|
||||||
|
<h1>{{ 'Report view : %name%' | trans({ '%name%' : entity.cFGroup.getName|localize_translatable_string } ) }}</h1>
|
||||||
|
|
||||||
|
<h2 class="chill-red">{{ 'Details'|trans }}</h2>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>{{ 'Person'|trans }} :</dt>
|
||||||
|
<dd>{{ entity.person }}</dd>
|
||||||
|
<dt>{{ 'Circle'|trans }} :</dt>
|
||||||
|
<dd><span class="scope circle">{{ entity.scope.name|localize_translatable_string }}</span></dd>
|
||||||
|
<dt>{{ 'Date'|trans }} :</dt>
|
||||||
|
<dd>{{ entity.date|format_date('long') }}</dd>
|
||||||
|
<dt>{{ 'User'|trans }} :</dt>
|
||||||
|
<dd>{{ entity.user }}</dd>
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h2 class="chill-red">{{ 'Report data'|trans }}</h2>
|
||||||
|
|
||||||
|
<dl class="chill_report_view_data">
|
||||||
|
{{ chill_custom_fields_group_widget(entity.cFData, entity.cFGroup) }}
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li class="cancel">
|
||||||
|
<a href="{{ path('report_list', { 'person_id': person.id } ) }}" class="sc-button bt-cancel">{{ 'Back to the list'|trans }}</a>
|
||||||
|
<li>
|
||||||
|
{% if is_granted('CHILL_REPORT_UPDATE', entity) %}
|
||||||
|
<li>
|
||||||
|
<a class="sc-button bt-edit" href="{{ path('report_edit', { 'person_id': entity.person.id, 'report_id': entity.id }) }}">{{ 'Update the report' | trans }}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,46 @@
|
|||||||
|
{#
|
||||||
|
* 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/>.
|
||||||
|
#}
|
||||||
|
<h2>{{ 'Reports search results'|trans }}</h2>
|
||||||
|
|
||||||
|
<p>{{ '%total% reports matching the search "%pattern%"'|transchoice( total, {'%pattern%': pattern, '%total%' : total}) }}</p>
|
||||||
|
|
||||||
|
{% if reports|length > 0 %}
|
||||||
|
<table class="striped rounded">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ 'Person'|trans }}</th>
|
||||||
|
<th>{{ 'Date'|trans }}</th>
|
||||||
|
<th>{{ 'Report type'|trans }}</th>
|
||||||
|
<th>{{ 'Scope'|trans }}</th>
|
||||||
|
<th> </th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for report in reports %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ report.person }}</td>
|
||||||
|
<td>{% if report.date %}{{ report.date|format_date('long') }}{% endif %}</td>
|
||||||
|
<td>{{ report.cFGroup.getName|localize_translatable_string }}</td>
|
||||||
|
<td>{{ report.scope.name|localize_translatable_string }}</td>
|
||||||
|
<td><a href="{{ path('report_view', { 'person_id': report.person.id, 'report_id': report.id }) }}">{{ 'View the report' | trans }}</a></td>
|
||||||
|
<td><a href="{{ path('report_edit', { 'person_id': report.person.id, 'report_id': report.id }) }}">{{ 'Update the report' | trans }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
@ -0,0 +1,40 @@
|
|||||||
|
<div class="report_entry">
|
||||||
|
<h3>{{ report.date|format_date('long') }}<span class="report"> / {{ 'Report'|trans }}</span></h3>
|
||||||
|
<div class="statement">
|
||||||
|
<span class="statement">{{ '%user% has filled a %report_label% report'|trans(
|
||||||
|
{
|
||||||
|
'%user%' : user,
|
||||||
|
'%report_label%': report.CFGroup.name|localize_translatable_string,
|
||||||
|
'%date%' : report.date|format_date('long') }
|
||||||
|
) }}</span>
|
||||||
|
</div>
|
||||||
|
{% if custom_fields_in_summary|length > 0 %}
|
||||||
|
<div class="summary">
|
||||||
|
<dl class="chill_report_view_data timeline">
|
||||||
|
{% for field in custom_fields_in_summary %}
|
||||||
|
{% if field.type == 'title' %}
|
||||||
|
{{ chill_custom_field_widget(report.cFData, field) }}
|
||||||
|
{% else %}
|
||||||
|
<dt>{{ chill_custom_field_label(field) }}</dt>
|
||||||
|
<dd>{{ chill_custom_field_widget(report.cFData, field) }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('report_view', { 'person_id': person.id, 'report_id': report.id} ) }}" class="sc-button bt-view">
|
||||||
|
{{ 'View the report'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if is_granted('CHILL_REPORT_UPDATE', report) %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('report_edit', { 'person_id': person.id, 'report_id': report.id} ) }}" class="sc-button bt-edit">
|
||||||
|
{{ 'Update the report'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
186
src/Bundle/ChillReport/Search/ReportSearch.php
Normal file
186
src/Bundle/ChillReport/Search/ReportSearch.php
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a suite of a modules, Chill is a software for social workers
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\Search;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Search\AbstractSearch;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
|
use Chill\MainBundle\Search\ParsingException;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search amongst reports
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportSearch extends AbstractSearch implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EntityManagerInterface
|
||||||
|
*/
|
||||||
|
private $em;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var AuthorizationHelper
|
||||||
|
*/
|
||||||
|
private $helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Chill\MainBundle\Entity\User
|
||||||
|
*/
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $em,
|
||||||
|
AuthorizationHelper $helper, TokenStorageInterface $tokenStorage)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->helper = $helper;
|
||||||
|
|
||||||
|
if(! $tokenStorage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User) {
|
||||||
|
throw new \RuntimeException('an user must be associated with token');
|
||||||
|
}
|
||||||
|
$this->user = $tokenStorage->getToken()->getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrder()
|
||||||
|
{
|
||||||
|
return 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActiveByDefault()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = array(), $format = 'html')
|
||||||
|
{
|
||||||
|
return $this->container->get('templating')->render('ChillReportBundle:Search:results.html.twig', array(
|
||||||
|
'reports' => $this->getReports($terms, $start, $limit),
|
||||||
|
'total' => $this->count($terms),
|
||||||
|
'pattern' => $this->recomposePattern($terms, array( 'date'), 'report')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getReports(array $terms, $start, $limit)
|
||||||
|
{
|
||||||
|
$qb = $this->buildQuery($terms);
|
||||||
|
|
||||||
|
$qb->select('r')
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->setFirstResult($start)
|
||||||
|
->orderBy('r.date', 'desc')
|
||||||
|
;
|
||||||
|
|
||||||
|
$reportQuery = $qb->getQuery();
|
||||||
|
$reportQuery->setFetchMode("Chill\ReportBundle\Entity\Report", "person", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
|
||||||
|
|
||||||
|
return $reportQuery->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function count(array $terms)
|
||||||
|
{
|
||||||
|
$qb = $this->buildQuery($terms);
|
||||||
|
|
||||||
|
$qb->select('COUNT(r.id)');
|
||||||
|
|
||||||
|
return $qb->getQuery()->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $terms the terms
|
||||||
|
* @return \Doctrine\ORM\QueryBuilder
|
||||||
|
*/
|
||||||
|
private function buildQuery(array $terms)
|
||||||
|
{
|
||||||
|
|
||||||
|
$query = $this->em->createQueryBuilder();
|
||||||
|
|
||||||
|
$query->from('ChillReportBundle:Report', 'r');
|
||||||
|
|
||||||
|
//throw a parsing exception if key 'date' and default is set
|
||||||
|
if (array_key_exists('date', $terms) && $terms['_default'] !== '') {
|
||||||
|
throw new ParsingException('You may not set a date argument and a date in default');
|
||||||
|
}
|
||||||
|
//throw a parsing exception if no argument except report
|
||||||
|
if (!array_key_exists('date', $terms) && $terms['_default'] === '') {
|
||||||
|
throw new ParsingException('You must provide either a date:YYYY-mm-dd argument or a YYYY-mm-dd default search');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (array_key_exists('date', $terms)) {
|
||||||
|
$query->andWhere($query->expr()->eq('r.date', ':date'))
|
||||||
|
->setParameter('date', $this->parseDate($terms['date']))
|
||||||
|
;
|
||||||
|
} elseif (array_key_exists('_default', $terms)) {
|
||||||
|
$query->andWhere($query->expr()->eq('r.date', ':date'))
|
||||||
|
->setParameter('date', $this->parseDate($terms['_default']))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->andWhere($this->addACL($query));
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addACL(QueryBuilder $qb)
|
||||||
|
{
|
||||||
|
//adding join
|
||||||
|
$qb->join('r.person', 'p');
|
||||||
|
|
||||||
|
$role = new Role('CHILL_REPORT_SEE');
|
||||||
|
$reachableCenters = $this->helper->getReachableCenters($this->user, $role);
|
||||||
|
|
||||||
|
$whereElement = $qb->expr()->orX();
|
||||||
|
$i = 0;
|
||||||
|
foreach ($reachableCenters as $center) {
|
||||||
|
$reachableScopesId = array_map(
|
||||||
|
function (Scope $scope) { return $scope->getId(); },
|
||||||
|
$this->helper->getReachableScopes($this->user, $role, $center)
|
||||||
|
);
|
||||||
|
$whereElement->add(
|
||||||
|
$qb->expr()->andX(
|
||||||
|
$qb->expr()->eq('p.center', ':center_'.$i),
|
||||||
|
$qb->expr()->in('r.scope', ':reachable_scopes_'.$i)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
$qb->setParameter('center_'.$i, $center);
|
||||||
|
$qb->setParameter('reachable_scopes_'.$i, $reachableScopesId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $whereElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports($domain, $format = 'html')
|
||||||
|
{
|
||||||
|
return $domain === 'report';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Julien Fastré <julien.fastre@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\ReportBundle\Security\Authorization;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
|
{
|
||||||
|
const CREATE = 'CHILL_REPORT_CREATE';
|
||||||
|
const SEE = 'CHILL_REPORT_SEE';
|
||||||
|
const UPDATE = 'CHILL_REPORT_UPDATE';
|
||||||
|
const LISTS = 'CHILL_REPORT_LISTS';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var AuthorizationHelper
|
||||||
|
*/
|
||||||
|
protected $helper;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(AuthorizationHelper $helper)
|
||||||
|
{
|
||||||
|
$this->helper = $helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function supports($attribute, $subject)
|
||||||
|
{
|
||||||
|
if ($subject instanceof Report) {
|
||||||
|
return \in_array($attribute, [
|
||||||
|
self::CREATE, self::UPDATE, self::SEE
|
||||||
|
]);
|
||||||
|
} elseif ($subject instanceof Center) {
|
||||||
|
return $attribute === self::LISTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||||
|
{
|
||||||
|
if (!$token->getUser() instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $this->helper->userHasAccess($token->getUser(), $subject, $attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRoles()
|
||||||
|
{
|
||||||
|
return [self::CREATE, self::UPDATE, self::SEE, self::LISTS];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRolesWithoutScope()
|
||||||
|
{
|
||||||
|
return array(self::LISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRolesWithHierarchy()
|
||||||
|
{
|
||||||
|
return [ 'Report' => $this->getRoles() ];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Julien Fastré <julien.fastre@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\ReportBundle\Tests\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||||
|
use Symfony\Component\BrowserKit\Client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is much well writtend than ReportControllerTest class, and will
|
||||||
|
* replace ReportControllerTest in the future.
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportControllerNextTest extends WebTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var Person
|
||||||
|
*/
|
||||||
|
protected $person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CustomFieldsGroup
|
||||||
|
*/
|
||||||
|
protected $group;
|
||||||
|
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
static::bootKernel();
|
||||||
|
// get person from fixture
|
||||||
|
$em = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager');
|
||||||
|
|
||||||
|
$this->person = $em
|
||||||
|
->getRepository('ChillPersonBundle:Person')
|
||||||
|
->findOneBy(array(
|
||||||
|
'lastName' => 'Charline',
|
||||||
|
'firstName' => 'Depardieu'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($this->person === NULL) {
|
||||||
|
throw new \RuntimeException("The expected person is not present in the database. "
|
||||||
|
. "Did you run `php app/console doctrine:fixture:load` before launching tests ? "
|
||||||
|
. "(expecting person is 'Charline Depardieu'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get custom fields group from fixture
|
||||||
|
$customFieldsGroups = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->findBy(array('entity' => 'Chill\ReportBundle\Entity\Report'))
|
||||||
|
;
|
||||||
|
//filter customFieldsGroup to get only "situation de logement"
|
||||||
|
$filteredCustomFieldsGroupHouse = array_filter($customFieldsGroups,
|
||||||
|
function(CustomFieldsGroup $group) {
|
||||||
|
return in_array("Situation de logement", $group->getName());
|
||||||
|
});
|
||||||
|
$this->group = $filteredCustomFieldsGroupHouse[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidCreate()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$form = $this->getReportForm($this->person, $this->group, $client);
|
||||||
|
|
||||||
|
$form->get('chill_reportbundle_report[date]')->setValue(
|
||||||
|
(new \DateTime())->format('d-m-Y'));
|
||||||
|
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isRedirect(),
|
||||||
|
"The next page is a redirection to the new report's view page");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUngrantedUserIsDeniedAccessOnListReports()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient('center b_social');
|
||||||
|
$client->request('GET', sprintf('/fr/person/%d/report/list',
|
||||||
|
$this->person->getId()));
|
||||||
|
|
||||||
|
$this->assertEquals(403, $client->getResponse()->getStatusCode(),
|
||||||
|
'assert that user for center b has a 403 status code when listing'
|
||||||
|
. 'reports on person from center a');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUngrantedUserIsDeniedAccessOnReport()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient('center b_social');
|
||||||
|
$reports = static::$kernel->getContainer()->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillReportBundle:Report')
|
||||||
|
->findBy(array('person' => $this->person));
|
||||||
|
$report = $reports[0];
|
||||||
|
|
||||||
|
$client->request('GET', sprintf('/fr/person/%d/report/%d/view',
|
||||||
|
$this->person->getId(), $report->getId()));
|
||||||
|
|
||||||
|
$this->assertEquals(403, $client->getResponse()->getStatusCode(),
|
||||||
|
'assert that user for center b has a 403 status code when '
|
||||||
|
. 'trying to watch a report from person from center a');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUngrantedUserIsDeniedReportNew()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient('center b_social');
|
||||||
|
|
||||||
|
$client->request('GET', sprintf('fr/person/%d/report/cfgroup/%d/new',
|
||||||
|
$this->person->getId(), $this->group->getId()));
|
||||||
|
|
||||||
|
$this->assertEquals(403, $client->getResponse()->getStatusCode(),
|
||||||
|
'assert that user is denied on trying to show a form "new" for'
|
||||||
|
. ' a person on another center');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUngrantedUserIsDeniedReportCreate()
|
||||||
|
{
|
||||||
|
$clientCenterA = $this->getAuthenticatedClient('center a_social');
|
||||||
|
|
||||||
|
$form = $this->getReportForm($this->person, $this->group, $clientCenterA);
|
||||||
|
|
||||||
|
$clientCenterB = $this->getAuthenticatedClient('center b_social');
|
||||||
|
$clientCenterB->submit($form);
|
||||||
|
|
||||||
|
$this->assertEquals(403, $clientCenterB->getResponse()->getStatusCode(),
|
||||||
|
'assert that user is denied on trying to show a form "new" for'
|
||||||
|
. ' a person on another center');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAuthenticatedClient($username = 'center a_social')
|
||||||
|
{
|
||||||
|
return static::createClient(array(), array(
|
||||||
|
'PHP_AUTH_USER' => $username,
|
||||||
|
'PHP_AUTH_PW' => 'password',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param Person $person
|
||||||
|
* @param CustomFieldsGroup $group
|
||||||
|
* @param Client $client
|
||||||
|
* @return \Symfony\Component\DomCrawler\Form
|
||||||
|
*/
|
||||||
|
protected function getReportForm(Person $person, CustomFieldsGroup $group, Client $client)
|
||||||
|
{
|
||||||
|
$url = sprintf('fr/person/%d/report/cfgroup/%d/new', $person->getId(),
|
||||||
|
$group->getId());
|
||||||
|
$crawler = $client->request('GET', $url);
|
||||||
|
|
||||||
|
return $crawler->selectButton('Ajouter le rapport')
|
||||||
|
->form();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
443
src/Bundle/ChillReport/Tests/Controller/ReportControllerTest.php
Normal file
443
src/Bundle/ChillReport/Tests/Controller/ReportControllerTest.php
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||||
|
* <http://www.champs-libres.coop>, <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\ReportBundle\Tests\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
use Symfony\Component\DomCrawler\Form;
|
||||||
|
use Symfony\Component\DomCrawler\Link;
|
||||||
|
use Symfony\Component\DomCrawler\Crawler;
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the life cycles of controllers, according to
|
||||||
|
* https://redmine.champs-libres.coop/projects/report/wiki/Test_plan_for_report_lifecycle
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportControllerTest extends WebTestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
const REPORT_NAME_FIELD = 'cFGroup';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Chill\PersonBundle\Entity\Person
|
||||||
|
*/
|
||||||
|
private static $person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \SClientymfony\Component\BrowserKit\
|
||||||
|
*/
|
||||||
|
private static $client;
|
||||||
|
|
||||||
|
private static $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CustomFieldsGroup
|
||||||
|
*/
|
||||||
|
private static $group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Doctrine\ORM\EntityManagerInterface
|
||||||
|
*/
|
||||||
|
private static $em;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
static::bootKernel();
|
||||||
|
|
||||||
|
static::$em = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager');
|
||||||
|
|
||||||
|
//get a random person
|
||||||
|
static::$person = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillPersonBundle:Person')
|
||||||
|
->findOneBy(array(
|
||||||
|
'lastName' => 'Charline',
|
||||||
|
'firstName' => 'Depardieu'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (static::$person === NULL) {
|
||||||
|
throw new \RuntimeException("The expected person is not present in the database. "
|
||||||
|
. "Did you run `php app/console doctrine:fixture:load` before launching tests ? "
|
||||||
|
. "(expecting person is 'Charline Depardieu'");
|
||||||
|
}
|
||||||
|
|
||||||
|
$customFieldsGroups = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->findBy(array('entity' => 'Chill\ReportBundle\Entity\Report'))
|
||||||
|
;
|
||||||
|
//filter customFieldsGroup to get only "situation de logement"
|
||||||
|
$filteredCustomFieldsGroupHouse = array_filter($customFieldsGroups,
|
||||||
|
function(CustomFieldsGroup $group) {
|
||||||
|
return in_array("Situation de logement", $group->getName());
|
||||||
|
});
|
||||||
|
static::$group = $filteredCustomFieldsGroupHouse[0];
|
||||||
|
|
||||||
|
static::$user = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillMainBundle:User')
|
||||||
|
->findOneBy(array('username' => "center a_social"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
static::$client = static::createClient(array(), array(
|
||||||
|
'PHP_AUTH_USER' => 'center a_social',
|
||||||
|
'PHP_AUTH_PW' => 'password',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type $username
|
||||||
|
* @return Client
|
||||||
|
*/
|
||||||
|
public function getAuthenticatedClient($username = 'center a_social')
|
||||||
|
{
|
||||||
|
return static::createClient(array(), array(
|
||||||
|
'PHP_AUTH_USER' => $username,
|
||||||
|
'PHP_AUTH_PW' => 'password',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the browser to be at a random person general page (/fr/person/%d/general),
|
||||||
|
* check if there is a menu link for adding a new report and return this link (as producer)
|
||||||
|
*
|
||||||
|
* We assume that :
|
||||||
|
* - we are on a "person" page
|
||||||
|
* - there are more than one report model
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testMenu()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$crawlerPersonPage = $client->request('GET', sprintf('/fr/person/%d/general',
|
||||||
|
static::$person->getId()));
|
||||||
|
|
||||||
|
if (! $client->getResponse()->isSuccessful()) {
|
||||||
|
var_dump($crawlerPersonPage->html());
|
||||||
|
throw new \RuntimeException('the request at person page failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$link = $crawlerPersonPage->selectLink("AJOUT D'UN RAPPORT")->link();
|
||||||
|
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $link,
|
||||||
|
"There is a \"add a report\" link in menu");
|
||||||
|
$this->assertContains(sprintf("/fr/person/%d/report/select/type/for/creation",
|
||||||
|
static::$person->getId()), $link->getUri(),
|
||||||
|
"There is a \"add a report\" link in menu");
|
||||||
|
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\DomCrawler\Link $link
|
||||||
|
* @return type
|
||||||
|
* @depends testMenu
|
||||||
|
*/
|
||||||
|
public function testChooseReportModelPage(Link $link)
|
||||||
|
{
|
||||||
|
// When I click on "add a report" link in menu
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$crawlerAddAReportPage = $client->click($link);
|
||||||
|
|
||||||
|
$form = $crawlerAddAReportPage->selectButton("Créer un nouveau rapport")->form();
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Symfony\Component\DomCrawler\Form', $form,
|
||||||
|
'I can see a form with a button "add a new report" ');
|
||||||
|
|
||||||
|
$this->assertGreaterThan(1, count($form->get(self::REPORT_NAME_FIELD)
|
||||||
|
->availableOptionValues()),
|
||||||
|
"I can choose between report models");
|
||||||
|
|
||||||
|
$possibleOptionsValue = $form->get(self::REPORT_NAME_FIELD)
|
||||||
|
->availableOptionValues();
|
||||||
|
$form->get(self::REPORT_NAME_FIELD)->setValue(
|
||||||
|
$possibleOptionsValue[array_rand($possibleOptionsValue)]);
|
||||||
|
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isRedirect());
|
||||||
|
return $client->followRedirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\DomCrawler\Crawler $crawlerNewReportPage
|
||||||
|
* @return type
|
||||||
|
* @depends testChooseReportModelPage
|
||||||
|
*/
|
||||||
|
public function testNewReportPage(Crawler $crawlerNewReportPage)
|
||||||
|
{
|
||||||
|
|
||||||
|
$addForm = $crawlerNewReportPage
|
||||||
|
->selectButton('Ajouter le rapport')
|
||||||
|
->form();
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Symfony\Component\DomCrawler\Form', $addForm,
|
||||||
|
'I have a report form');
|
||||||
|
|
||||||
|
$this->isFormAsExpected($addForm);
|
||||||
|
|
||||||
|
return $addForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a form for report new
|
||||||
|
*
|
||||||
|
* @param \Chill\ReportBundle\Tests\Controller\Person $person
|
||||||
|
* @param CustomFieldsGroup $group
|
||||||
|
* @param \Symfony\Component\BrowserKit\Client $client
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function getReportForm(Person $person, CustomFieldsGroup $group,
|
||||||
|
\Symfony\Component\BrowserKit\Client $client)
|
||||||
|
{
|
||||||
|
$url = sprintf('fr/person/%d/report/cfgroup/%d/new', $person->getId(),
|
||||||
|
$group->getId());
|
||||||
|
$crawler = $client->request('GET', $url);
|
||||||
|
|
||||||
|
return $crawler->selectButton('Ajouter le rapport')
|
||||||
|
->form();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the expected field are present
|
||||||
|
*
|
||||||
|
* @param Form $form
|
||||||
|
* @param boolean $isDefault if the form should be at default values
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function isFormAsExpected(Form $form, $isDefault = true)
|
||||||
|
{
|
||||||
|
$this->assertTrue($form->has('chill_reportbundle_report[date]'),
|
||||||
|
'the report form have a field "date"' );
|
||||||
|
$this->assertTrue($form->has('chill_reportbundle_report[user]'),
|
||||||
|
'the report form have field "user" ');
|
||||||
|
|
||||||
|
$this->assertEquals('select', $form->get('chill_reportbundle_report[user]')
|
||||||
|
->getType(), "the user field is a select input");
|
||||||
|
|
||||||
|
if ($isDefault) {
|
||||||
|
$date = new \DateTime('now');
|
||||||
|
$this->assertEquals(
|
||||||
|
$date->format('d-m-Y'),
|
||||||
|
$form->get('chill_reportbundle_report[date]')->getValue(),
|
||||||
|
"the date field contains the current date by default"
|
||||||
|
);
|
||||||
|
|
||||||
|
//resolve the user
|
||||||
|
$userId = $form->get('chill_reportbundle_report[user]')->getValue();
|
||||||
|
|
||||||
|
$this->assertEquals('center a_social', static::$user->getUsername(),
|
||||||
|
"the user field is the current user by default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fill the form with correct data
|
||||||
|
*
|
||||||
|
* @param Form $form
|
||||||
|
*/
|
||||||
|
private function fillCorrectForm(Form $form)
|
||||||
|
{
|
||||||
|
$form->get('chill_reportbundle_report[date]')->setValue(
|
||||||
|
(new \DateTime())->format('d-m-Y'));
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that setting a Null date redirect to an error page
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testNullDate()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$form = $this->getReportForm(static::$person, static::$group,
|
||||||
|
$client);
|
||||||
|
//var_dump($form);
|
||||||
|
$filledForm = $this->fillCorrectForm($form);
|
||||||
|
$filledForm->get('chill_reportbundle_report[date]')->setValue('');
|
||||||
|
//$this->markTestSkipped();
|
||||||
|
$crawler = $this->getAuthenticatedClient('center a_administrative')->submit($filledForm);
|
||||||
|
|
||||||
|
$this->assertFalse($client->getResponse()->isRedirect());
|
||||||
|
$this->assertGreaterThan(0, $crawler->filter('.error')->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that setting a Null date redirect to an error page
|
||||||
|
*
|
||||||
|
* @param Form $form
|
||||||
|
* @depends testNewReportPage
|
||||||
|
*/
|
||||||
|
public function testInvalidDate(Form $form)
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$this->markTestSkipped("This test raise an error since symfony 2.7. "
|
||||||
|
. "The user is not correctly reloaded from database.");
|
||||||
|
$filledForm = $this->fillCorrectForm($form);
|
||||||
|
$filledForm->get('chill_reportbundle_report[date]')->setValue('invalid date value');
|
||||||
|
|
||||||
|
$crawler = $client->submit($filledForm);
|
||||||
|
|
||||||
|
$this->assertFalse($client->getResponse()->isRedirect());
|
||||||
|
$this->assertGreaterThan(0, $crawler->filter('.error')->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a incorrect value in user will show an error page
|
||||||
|
*
|
||||||
|
* @depends testNewReportPage
|
||||||
|
* @param Form $form
|
||||||
|
*/
|
||||||
|
public function testInvalidUser(Form $form)
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$filledForm = $this->fillCorrectForm($form);
|
||||||
|
$select = $filledForm->get('chill_reportbundle_report[user]')
|
||||||
|
->disableValidation()
|
||||||
|
->setValue(-1);
|
||||||
|
|
||||||
|
$crawler = $client->submit($filledForm);
|
||||||
|
|
||||||
|
$this->assertFalse($client->getResponse()->isRedirect());
|
||||||
|
$this->assertGreaterThan(0, $crawler->filter('.error')->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the creation of a report
|
||||||
|
*
|
||||||
|
* depends testNewReportPage
|
||||||
|
* param Form $form
|
||||||
|
*/
|
||||||
|
public function testValidCreate()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
//$this->markTestSkipped("This test raise an error since symfony 2.7. "
|
||||||
|
// . "The user is not correctly reloaded from database.");
|
||||||
|
$addForm = $this->getReportForm(self::$person, self::$group, $client);
|
||||||
|
$filledForm = $this->fillCorrectForm($addForm);
|
||||||
|
$c = $client->submit($filledForm);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isRedirect(),
|
||||||
|
"The next page is a redirection to the new report's view page");
|
||||||
|
$client->followRedirect();
|
||||||
|
|
||||||
|
$this->assertRegExp("|/fr/person/".static::$person->getId()."/report/[0-9]*/view$|",
|
||||||
|
$client->getHistory()->current()->getUri(),
|
||||||
|
"The next page is a redirection to the new report's view page");
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
preg_match('|/report/([0-9]*)/view$|',
|
||||||
|
$client->getHistory()->current()->getUri(), $matches);
|
||||||
|
|
||||||
|
return $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testValidCreate
|
||||||
|
* @param int $reportId
|
||||||
|
*/
|
||||||
|
public function testList($reportId)
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$crawler = $client->request('GET', sprintf('/fr/person/%s/report/list',
|
||||||
|
static::$person->getId()));
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$linkSee = $crawler->filter('.bt-view')->links();
|
||||||
|
$this->assertGreaterThan(0, count($linkSee));
|
||||||
|
$this->assertRegExp(sprintf('|/fr/person/%s/report/[0-9]*/view$|',
|
||||||
|
static::$person->getId(), $reportId), $linkSee[0]->getUri());
|
||||||
|
|
||||||
|
$linkUpdate = $crawler->filter('.bt-update')->links();
|
||||||
|
$this->assertGreaterThan(0, count($linkUpdate));
|
||||||
|
$this->assertRegExp(sprintf('|/fr/person/%s/report/[0-9]*/edit$|',
|
||||||
|
static::$person->getId(), $reportId), $linkUpdate[0]->getUri());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the view of a report
|
||||||
|
*
|
||||||
|
* @depends testValidCreate
|
||||||
|
* @param int $reportId
|
||||||
|
*/
|
||||||
|
public function testView($reportId)
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$client->request('GET',
|
||||||
|
sprintf('/fr/person/%s/report/%s/view', static::$person->getId(), $reportId));
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful(),
|
||||||
|
'the page is shown');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test the update form
|
||||||
|
*
|
||||||
|
* @depends testValidCreate
|
||||||
|
* @param int $reportId
|
||||||
|
*/
|
||||||
|
public function testUpdate($reportId)
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
$crawler = $client->request('GET',
|
||||||
|
sprintf('/fr/person/%s/report/%s/edit', static::$person->getId(), $reportId));
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$form = $crawler
|
||||||
|
->selectButton('Enregistrer le rapport')
|
||||||
|
->form();
|
||||||
|
|
||||||
|
$form->get('chill_reportbundle_report[date]')->setValue(
|
||||||
|
(new \DateTime('yesterday'))->format('d-m-Y'));
|
||||||
|
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isRedirect(
|
||||||
|
sprintf('/fr/person/%s/report/%s/view',
|
||||||
|
static::$person->getId(), $reportId)));
|
||||||
|
|
||||||
|
$this->assertEquals(new \DateTime('yesterday'), static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager')
|
||||||
|
->getRepository('ChillReportBundle:Report')
|
||||||
|
->find($reportId)
|
||||||
|
->getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\Tests\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
class ChillReportExtensionTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check if class Chill\ReportBundle\Entity\Report is in chill_custom_fields.customizables_entities
|
||||||
|
*/
|
||||||
|
public function testDeclareReportAsCustomizable()
|
||||||
|
{
|
||||||
|
self::bootKernel(array('environment' => 'test'));
|
||||||
|
$customizablesEntities = static::$kernel->getContainer()
|
||||||
|
->getParameter('chill_custom_fields.customizables_entities');
|
||||||
|
|
||||||
|
$reportFounded = false;
|
||||||
|
foreach ($customizablesEntities as $customizablesEntity) {
|
||||||
|
if($customizablesEntity['class'] === 'Chill\ReportBundle\Entity\Report') {
|
||||||
|
$reportFounded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(! $reportFounded) {
|
||||||
|
throw new Exception("Class Chill\ReportBundle\Entity\Report not found in chill_custom_fields.customizables_entities", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
src/Bundle/ChillReport/Tests/Search/ReportSearchTest.php
Normal file
126
src/Bundle/ChillReport/Tests/Search/ReportSearchTest.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a suite of a modules, Chill is a software for social workers
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ReportBundle\Tests\Search;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for report search
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportSearchTest extends WebTestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testSearchExpectedDefault()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/search', array(
|
||||||
|
'q' => '@report 2015-01-05'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertRegExp('/Situation de logement/i', $crawler->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNamedSearch()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/search', array(
|
||||||
|
'q' => '@report '.(new \DateTime('tomorrow'))->format('Y-m-d'), //there should be any result for future. And future is tomorrow
|
||||||
|
'name' => 'report'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSearchByDate()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/search', array(
|
||||||
|
'q' => '@report date:2015-01-05'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertRegExp('/Situation de logement/i', $crawler->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSearchDoubleDate()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/search', array(
|
||||||
|
'q' => '@report date:2014-05-01 2014-05-06'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertGreaterThan(0, $crawler->filter('.error')->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSearchEmtpy()
|
||||||
|
{
|
||||||
|
$client = $this->getAuthenticatedClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/search', array(
|
||||||
|
'q' => '@report '
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertGreaterThan(0, $crawler->filter('.error')->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that the user do not see unauthorized results
|
||||||
|
*
|
||||||
|
* We test for that that :
|
||||||
|
* - we do not see any unauthorized scope mention
|
||||||
|
*/
|
||||||
|
public function testUsersDoNotSeeUnauthorizedResults()
|
||||||
|
{
|
||||||
|
$clientSocial = $this->getAuthenticatedClient();
|
||||||
|
$clientAdministrative = $this->getAuthenticatedClient('center a_administrative');
|
||||||
|
|
||||||
|
$params = array('q' => '@report date:2015-01-05');
|
||||||
|
|
||||||
|
$crawlerSocial = $clientSocial->request('GET', '/fr/search', $params);
|
||||||
|
$crawlerAdministrative = $clientAdministrative->request('GET', '/fr/search', $params);
|
||||||
|
|
||||||
|
|
||||||
|
$this->assertNotContains('social', $crawlerAdministrative->filter('.content')
|
||||||
|
->text());
|
||||||
|
$this->assertNotContains('administrative', $crawlerAdministrative->filter('.content')
|
||||||
|
->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return \Symfony\Component\BrowserKit\Client
|
||||||
|
*/
|
||||||
|
private function getAuthenticatedClient($username = 'center a_social')
|
||||||
|
{
|
||||||
|
return static::createClient(array(), array(
|
||||||
|
'PHP_AUTH_USER' => $username,
|
||||||
|
'PHP_AUTH_PW' => 'password',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Julien Fastré <julien.fastre@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\ReportBundle\Tests\Security\Authorization;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Chill\MainBundle\Test\PrepareUserTrait;
|
||||||
|
use Chill\MainBundle\Test\PrepareCenterTrait;
|
||||||
|
use Chill\MainBundle\Test\PrepareScopeTrait;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class ReportVoterTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
use PrepareUserTrait, PrepareCenterTrait, PrepareScopeTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Chill\ReportBundle\Security\Authorization\ReportVoter
|
||||||
|
*/
|
||||||
|
protected $voter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Prophecy\Prophet
|
||||||
|
*/
|
||||||
|
protected $prophet;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
static::bootKernel();
|
||||||
|
$this->voter = static::$kernel->getContainer()
|
||||||
|
->get('chill.report.security.authorization.report_voter');
|
||||||
|
$this->prophet = new \Prophecy\Prophet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataProvider
|
||||||
|
* @param type $expectedResult
|
||||||
|
* @param Report $report
|
||||||
|
* @param User $user
|
||||||
|
* @param type $action
|
||||||
|
* @param type $message
|
||||||
|
*/
|
||||||
|
public function testAccess($expectedResult, Report $report, $action,
|
||||||
|
$message, User $user = null)
|
||||||
|
{
|
||||||
|
$token = $this->prepareToken($user);
|
||||||
|
$result = $this->voter->vote($token, $report, [$action]);
|
||||||
|
$this->assertEquals($expectedResult, $result, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prepare a person
|
||||||
|
*
|
||||||
|
* The only properties set is the center, others properties are ignored.
|
||||||
|
*
|
||||||
|
* @param Center $center
|
||||||
|
* @return Person
|
||||||
|
*/
|
||||||
|
protected function preparePerson(Center $center)
|
||||||
|
{
|
||||||
|
return (new Person())
|
||||||
|
->setCenter($center)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prepare a token interface with correct rights
|
||||||
|
*
|
||||||
|
* if $permissions = null, user will be null (no user associated with token
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
* @return \Symfony\Component\Security\Core\Authentication\Token\TokenInterface
|
||||||
|
*/
|
||||||
|
protected function prepareToken(User $user = null)
|
||||||
|
{
|
||||||
|
$token = $this->prophet->prophesize();
|
||||||
|
$token
|
||||||
|
->willImplement('\Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||||
|
if ($user === NULL) {
|
||||||
|
$token->getUser()->willReturn(null);
|
||||||
|
} else {
|
||||||
|
$token->getUser()->willReturn($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $token->reveal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataProvider()
|
||||||
|
{
|
||||||
|
$centerA = $this->prepareCenter(1, 'center A');
|
||||||
|
$centerB = $this->prepareCenter(2, 'center B');
|
||||||
|
$scopeA = $this->prepareScope(1, 'scope default');
|
||||||
|
$scopeB = $this->prepareScope(2, 'scope B');
|
||||||
|
$scopeC = $this->prepareScope(3, 'scope C');
|
||||||
|
|
||||||
|
$userA = $this->prepareUser(array(
|
||||||
|
array(
|
||||||
|
'center' => $centerA,
|
||||||
|
'permissionsGroup' => array(
|
||||||
|
['scope' => $scopeB, 'role' => 'CHILL_REPORT_SEE'],
|
||||||
|
['scope' => $scopeA, 'role' => 'CHILL_REPORT_UPDATE']
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'center' => $centerB,
|
||||||
|
'permissionsGroup' => array(
|
||||||
|
['scope' => $scopeA, 'role' => 'CHILL_REPORT_SEE'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
));
|
||||||
|
|
||||||
|
$reportA = (new Report)
|
||||||
|
->setScope($scopeA)
|
||||||
|
->setPerson($this->preparePerson($centerA))
|
||||||
|
;
|
||||||
|
$reportB = (new Report())
|
||||||
|
->setScope($scopeB)
|
||||||
|
->setPerson($this->preparePerson($centerA))
|
||||||
|
;
|
||||||
|
$reportC = (new Report())
|
||||||
|
->setScope($scopeC)
|
||||||
|
->setPerson($this->preparePerson($centerB))
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
VoterInterface::ACCESS_DENIED,
|
||||||
|
$reportA,
|
||||||
|
'CHILL_REPORT_SEE',
|
||||||
|
"assert is denied to a null user",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
VoterInterface::ACCESS_GRANTED,
|
||||||
|
$reportA,
|
||||||
|
'CHILL_REPORT_SEE',
|
||||||
|
"assert access is granted to a user with inheritance UPDATE > SEE",
|
||||||
|
$userA
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
VoterInterface::ACCESS_GRANTED,
|
||||||
|
$reportB,
|
||||||
|
'CHILL_REPORT_SEE',
|
||||||
|
"assert access is granted to a user without inheritance",
|
||||||
|
$userA
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
VoterInterface::ACCESS_DENIED,
|
||||||
|
$reportC,
|
||||||
|
'CHILL_REPORT_SEE',
|
||||||
|
'assert access is denied to a report',
|
||||||
|
$userA
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
187
src/Bundle/ChillReport/Tests/Timeline/TimelineProviderTest.php
Normal file
187
src/Bundle/ChillReport/Tests/Timeline/TimelineProviderTest.php
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
* Copyright (C) 2015 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\ReportBundle\Tests\Timeline;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
use Chill\MainBundle\Tests\TestHelper as MainTestHelper;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a report is shown into timeline
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
* @author Champs Libres <info@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class TimelineProviderTest extends WebTestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Doctrine\ORM\EntityManager
|
||||||
|
*/
|
||||||
|
private static $em;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var Person
|
||||||
|
*/
|
||||||
|
private $person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var Report
|
||||||
|
*/
|
||||||
|
private $report;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a person with a report associated with the person
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
static::bootKernel();
|
||||||
|
|
||||||
|
static::$em = static::$kernel->getContainer()
|
||||||
|
->get('doctrine.orm.entity_manager');
|
||||||
|
|
||||||
|
$center = static::$em->getRepository('ChillMainBundle:Center')
|
||||||
|
->findOneBy(array('name' => 'Center A'));
|
||||||
|
|
||||||
|
$person = (new Person(new \DateTime('2015-05-01')))
|
||||||
|
->setGender(Person::FEMALE_GENDER)
|
||||||
|
->setFirstName('Nelson')
|
||||||
|
->setLastName('Mandela')
|
||||||
|
->setCenter($center);
|
||||||
|
static::$em->persist($person);
|
||||||
|
$this->person = $person;
|
||||||
|
|
||||||
|
$scopesSocial = array_filter(static::$em
|
||||||
|
->getRepository('ChillMainBundle:Scope')
|
||||||
|
->findAll(),
|
||||||
|
function(Scope $scope) { return $scope->getName()['en'] === 'social'; })
|
||||||
|
;
|
||||||
|
|
||||||
|
$report = (new Report)
|
||||||
|
->setUser(static::$em->getRepository('ChillMainBundle:User')
|
||||||
|
->findOneByUsername('center a_social'))
|
||||||
|
->setDate(new \DateTime('2015-05-02'))
|
||||||
|
->setPerson($this->person)
|
||||||
|
->setCFGroup($this->getHousingCustomFieldsGroup())
|
||||||
|
->setCFData(['has_logement' => 'own_house',
|
||||||
|
'house-desc' => 'blah blah'])
|
||||||
|
->setScope(end($scopesSocial));
|
||||||
|
|
||||||
|
static::$em->persist($report);
|
||||||
|
$this->report = $report;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static::$em->flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a report is shown in timeline
|
||||||
|
*/
|
||||||
|
public function testTimelineReport()
|
||||||
|
{
|
||||||
|
$client = static::createClient(array(),
|
||||||
|
MainTestHelper::getAuthenticatedClientOptions()
|
||||||
|
);
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/person/'.$this->person->getId()
|
||||||
|
.'/timeline');
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful(),
|
||||||
|
'The page timeline is loaded successfully');
|
||||||
|
$this->assertContains('a ajouté un rapport', $crawler->text(),
|
||||||
|
'the page contains the text "a publié un rapport"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTimelineReportWithSummaryField()
|
||||||
|
{
|
||||||
|
//load the page
|
||||||
|
$client = static::createClient(array(),
|
||||||
|
MainTestHelper::getAuthenticatedClientOptions()
|
||||||
|
);
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/person/'.$this->person->getId()
|
||||||
|
.'/timeline');
|
||||||
|
|
||||||
|
//performs tests
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful(),
|
||||||
|
'The page timeline is loaded successfully');
|
||||||
|
$this->assertGreaterThan(0, $crawler->filter('.report_entry .summary')
|
||||||
|
->count(),
|
||||||
|
'the page contains a .report .summary element');
|
||||||
|
$this->assertContains('blah blah', $crawler->filter('.report_entry .summary')
|
||||||
|
->text(),
|
||||||
|
'the page contains the text "blah blah"');
|
||||||
|
$this->assertContains('Propriétaire', $crawler->filter('.report_entry .summary')
|
||||||
|
->text(),
|
||||||
|
'the page contains the mention "Propriétaire"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReportIsNotVisibleToUngrantedUsers()
|
||||||
|
{
|
||||||
|
$client = static::createClient(array(),
|
||||||
|
MainTestHelper::getAuthenticatedClientOptions('center a_administrative')
|
||||||
|
);
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/fr/person/'.$this->person->getId()
|
||||||
|
.'/timeline');
|
||||||
|
|
||||||
|
$this->assertEquals(0, $crawler->filter('.report_entry .summary')
|
||||||
|
->count(),
|
||||||
|
'the page does not contains a .report .summary element');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a random custom fields group
|
||||||
|
*
|
||||||
|
* @return \Chill\CustomFieldsBundle\Entity\CustomFieldsGroup
|
||||||
|
*/
|
||||||
|
private function getHousingCustomFieldsGroup()
|
||||||
|
{
|
||||||
|
$groups = static::$em
|
||||||
|
->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
if ($group->getName()['fr'] === 'Situation de logement') {
|
||||||
|
return $group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups[rand(0, count($groups) -1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
//static::$em->refresh($this->person);
|
||||||
|
//static::$em->refresh($this->report);
|
||||||
|
// static::$em->remove($this->person);
|
||||||
|
//static::$em->remove($this->report);
|
||||||
|
}
|
||||||
|
}
|
7
src/Bundle/ChillReport/Tests/bootstrap.php
Normal file
7
src/Bundle/ChillReport/Tests/bootstrap.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
|
||||||
|
throw new \LogicException('Could not find autoload.php in vendor/. Did you run "composer install --dev"?');
|
||||||
|
}
|
||||||
|
|
||||||
|
require $autoloadFile;
|
281
src/Bundle/ChillReport/Timeline/TimelineReportProvider.php
Normal file
281
src/Bundle/ChillReport/Timeline/TimelineReportProvider.php
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
* Copyright (C) 2015 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\ReportBundle\Timeline;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Timeline\TimelineProviderInterface;
|
||||||
|
use Doctrine\ORM\EntityManager;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Chill\CustomFieldsBundle\Service\CustomFieldsHelper;
|
||||||
|
use Chill\ReportBundle\Entity\Report;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide report for inclusion in timeline
|
||||||
|
*
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
* @author Champs Libres <info@champs-libres.coop>
|
||||||
|
*/
|
||||||
|
class TimelineReportProvider implements TimelineProviderInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var EntityManager
|
||||||
|
*/
|
||||||
|
protected $em;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var AuthorizationHelper
|
||||||
|
*/
|
||||||
|
protected $helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var \Chill\MainBundle\Entity\User
|
||||||
|
*/
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CustomFieldsHelper
|
||||||
|
*/
|
||||||
|
protected $customFieldsHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var
|
||||||
|
*/
|
||||||
|
protected $showEmptyValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TimelineReportProvider constructor.
|
||||||
|
*
|
||||||
|
* @param EntityManager $em
|
||||||
|
* @param AuthorizationHelper $helper
|
||||||
|
* @param TokenStorageInterface $storage
|
||||||
|
* @param CustomFieldsHelper $customFieldsHelper
|
||||||
|
* @param $showEmptyValues
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
EntityManager $em,
|
||||||
|
AuthorizationHelper $helper,
|
||||||
|
TokenStorageInterface $storage,
|
||||||
|
CustomFieldsHelper $customFieldsHelper,
|
||||||
|
$showEmptyValues
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->helper = $helper;
|
||||||
|
|
||||||
|
if (!$storage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('A user should be authenticated !');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->user = $storage->getToken()->getUser();
|
||||||
|
$this->customFieldsHelper = $customFieldsHelper;
|
||||||
|
$this->showEmptyValues = $showEmptyValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function fetchQuery($context, array $args)
|
||||||
|
{
|
||||||
|
$this->checkContext($context);
|
||||||
|
|
||||||
|
$metadataReport = $this->em->getClassMetadata('ChillReportBundle:Report');
|
||||||
|
$metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => $metadataReport->getTableName()
|
||||||
|
.'.'.$metadataReport->getColumnName('id'),
|
||||||
|
'type' => 'report',
|
||||||
|
'date' => $metadataReport->getTableName()
|
||||||
|
.'.'.$metadataReport->getColumnName('date'),
|
||||||
|
'FROM' => $this->getFromClause($metadataReport, $metadataPerson),
|
||||||
|
'WHERE' => $this->getWhereClause($metadataReport, $metadataPerson,
|
||||||
|
$args['person'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getWhereClause(ClassMetadata $metadataReport,
|
||||||
|
ClassMetadata $metadataPerson, Person $person)
|
||||||
|
{
|
||||||
|
$role = new Role('CHILL_REPORT_SEE');
|
||||||
|
$reachableCenters = $this->helper->getReachableCenters($this->user,
|
||||||
|
$role);
|
||||||
|
$associationMapping = $metadataReport->getAssociationMapping('person');
|
||||||
|
|
||||||
|
// we start with reports having the person_id linked to person
|
||||||
|
// (currently only context "person" is supported)
|
||||||
|
$whereClause = sprintf('%s = %d',
|
||||||
|
$associationMapping['joinColumns'][0]['name'],
|
||||||
|
$person->getId());
|
||||||
|
|
||||||
|
// we add acl (reachable center and scopes)
|
||||||
|
$centerAndScopeLines = array();
|
||||||
|
foreach ($reachableCenters as $center) {
|
||||||
|
$reachablesScopesId = array_map(
|
||||||
|
function(Scope $scope) { return $scope->getId(); },
|
||||||
|
$this->helper->getReachableScopes($this->user, $role,
|
||||||
|
$person->getCenter())
|
||||||
|
);
|
||||||
|
|
||||||
|
$centerAndScopeLines[] = sprintf('(%s = %d AND %s IN (%s))',
|
||||||
|
$metadataPerson->getTableName().'.'.
|
||||||
|
$metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'],
|
||||||
|
$center->getId(),
|
||||||
|
$metadataReport->getTableName().'.'.
|
||||||
|
$metadataReport->getAssociationMapping('scope')['joinColumns'][0]['name'],
|
||||||
|
implode(',', $reachablesScopesId));
|
||||||
|
|
||||||
|
}
|
||||||
|
$whereClause .= ' AND ('.implode(' OR ', $centerAndScopeLines).')';
|
||||||
|
|
||||||
|
return $whereClause;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFromClause(ClassMetadata $metadataReport,
|
||||||
|
ClassMetadata $metadataPerson)
|
||||||
|
{
|
||||||
|
$associationMapping = $metadataReport->getAssociationMapping('person');
|
||||||
|
|
||||||
|
return $metadataReport->getTableName().' JOIN '
|
||||||
|
.$metadataPerson->getTableName().' ON '
|
||||||
|
.$metadataPerson->getTableName().'.'.
|
||||||
|
$associationMapping['joinColumns'][0]['referencedColumnName']
|
||||||
|
.' = '
|
||||||
|
.$associationMapping['joinColumns'][0]['name']
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getEntities(array $ids)
|
||||||
|
{
|
||||||
|
$reports = $this->em->getRepository('ChillReportBundle:Report')
|
||||||
|
->findBy(array('id' => $ids));
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach($reports as $report) {
|
||||||
|
$result[$report->getId()] = $report;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getEntityTemplate($entity, $context, array $args)
|
||||||
|
{
|
||||||
|
$this->checkContext($context);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'template' => 'ChillReportBundle:Timeline:report_person_context.html.twig',
|
||||||
|
'template_data' => array(
|
||||||
|
'report' => $entity,
|
||||||
|
'custom_fields_in_summary' => $this->getFieldsToRender($entity, $context),
|
||||||
|
'person' => $args['person'],
|
||||||
|
'user' => $entity->getUser()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getFieldsToRender(Report $entity, $context, array $args = array())
|
||||||
|
{
|
||||||
|
//gather all custom fields which should appears in summary
|
||||||
|
$gatheredFields = array();
|
||||||
|
|
||||||
|
if (array_key_exists('summary_fields', $entity->getCFGroup()->getOptions())) {
|
||||||
|
// keep in memory title
|
||||||
|
$title = null;
|
||||||
|
$subtitle = null;
|
||||||
|
|
||||||
|
foreach ($entity->getCFGroup()->getCustomFields() as $customField) {
|
||||||
|
if (in_array($customField->getSlug(),
|
||||||
|
$entity->getCFGroup()->getOptions()['summary_fields'])) {
|
||||||
|
// if we do not want to show empty values
|
||||||
|
if ($this->showEmptyValues === false) {
|
||||||
|
if ($customField->getType() === 'title') {
|
||||||
|
$options = $customField->getOptions();
|
||||||
|
switch($options['type']) {
|
||||||
|
case 'title': $title = $customField; break;
|
||||||
|
case 'subtitle': $subtitle = $customField; break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->customFieldsHelper->isEmptyValue($entity->getCFData(), $customField)
|
||||||
|
=== false) {
|
||||||
|
if ($title !== NULL) {
|
||||||
|
$gatheredFields[] = $title;
|
||||||
|
$title = null;
|
||||||
|
}
|
||||||
|
if ($subtitle !== NULL) {
|
||||||
|
$gatheredFields[] = $subtitle;
|
||||||
|
$subtitle = null;
|
||||||
|
}
|
||||||
|
$gatheredFields[] = $customField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$gatheredFields[] = $customField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $gatheredFields;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function supportsType($type)
|
||||||
|
{
|
||||||
|
return $type === 'report';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the context is supported
|
||||||
|
*
|
||||||
|
* @param string $context
|
||||||
|
* @throws \LogicException if the context is not supported
|
||||||
|
*/
|
||||||
|
private function checkContext($context)
|
||||||
|
{
|
||||||
|
if ($context !== 'person') {
|
||||||
|
throw new \LogicException("The context '$context' is not "
|
||||||
|
. "supported. Currently only 'person' is supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/Bundle/ChillReport/apigen.neon
Normal file
11
src/Bundle/ChillReport/apigen.neon
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# configuration for apigen
|
||||||
|
|
||||||
|
source:
|
||||||
|
- .
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- vendor/*
|
||||||
|
- Test*
|
||||||
|
- Resources/test
|
||||||
|
|
||||||
|
title: Chill Report Bundle
|
41
src/Bundle/ChillReport/composer.json
Normal file
41
src/Bundle/ChillReport/composer.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "chill-project/report",
|
||||||
|
"description": "The bundle for reports",
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"keywords" : ["chill", "social work"],
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"homepage" : "https://github.com/Chill-project/Report",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "Chill\\ReportBundle\\": "" }
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"classmap": [ "Resources/test/Fixtures/App/app/AppKernel.php" ]
|
||||||
|
},
|
||||||
|
"authors" : [
|
||||||
|
{
|
||||||
|
"name": "Champs-Libres",
|
||||||
|
"email": "info@champs-libres.coop",
|
||||||
|
"homepage": "http://www.champs-libres.coop"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"post-install-cmd": [
|
||||||
|
"ComposerBundleMigration\\Composer\\Migrations::synchronizeMigrations",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap"
|
||||||
|
],
|
||||||
|
"post-update-cmd": [
|
||||||
|
"ComposerBundleMigration\\Composer\\Migrations::synchronizeMigrations",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"app-migrations-dir": "Resources/test/Fixtures/App/app/DoctrineMigrations",
|
||||||
|
"symfony-app-dir": "Tests/Fixtures/App/app"
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
51
src/Bundle/ChillReport/config/routes.yaml
Normal file
51
src/Bundle/ChillReport/config/routes.yaml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
report_select_type:
|
||||||
|
path: /{_locale}/person/{person_id}/report/select/type/for/creation
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:selectReportType" }
|
||||||
|
options:
|
||||||
|
menus:
|
||||||
|
person:
|
||||||
|
order: 100
|
||||||
|
label: Add a report
|
||||||
|
report_new:
|
||||||
|
path: /{_locale}/person/{person_id}/report/cfgroup/{cf_group_id}/new
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:new" }
|
||||||
|
|
||||||
|
report_create:
|
||||||
|
path: /{_locale}/person/{person_id}/report/cfgroup/{cf_group_id}/create
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:create" }
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
report_list:
|
||||||
|
path: /{_locale}/person/{person_id}/report/list
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:list" }
|
||||||
|
options:
|
||||||
|
menus:
|
||||||
|
person:
|
||||||
|
order: 101
|
||||||
|
label: Report list
|
||||||
|
report_view:
|
||||||
|
path: /{_locale}/person/{person_id}/report/{report_id}/view
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:view" }
|
||||||
|
|
||||||
|
report_edit:
|
||||||
|
path: /{_locale}/person/{person_id}/report/{report_id}/edit
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:edit" }
|
||||||
|
|
||||||
|
report_update:
|
||||||
|
path: /{_locale}/person/{person_id}/report/{report_id}/update
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:update" }
|
||||||
|
methods: [POST, PUT]
|
||||||
|
|
||||||
|
report_export_list:
|
||||||
|
path: /{_locale}/export/report/cfgroup/{cf_group_id}
|
||||||
|
defaults: { _controller: "ChillReportBundle:Report:export" }
|
||||||
|
|
||||||
|
|
||||||
|
report_export_select_type:
|
||||||
|
path: /{_locale}/export/report/select/type
|
||||||
|
defaults: {_controller: "ChillReportBundle:Report:selectReportTypeForExport" }
|
||||||
|
options:
|
||||||
|
menus:
|
||||||
|
export:
|
||||||
|
order: 100
|
||||||
|
label: Export reports
|
46
src/Bundle/ChillReport/config/services.yaml
Normal file
46
src/Bundle/ChillReport/config/services.yaml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
services:
|
||||||
|
# chill_report.example:
|
||||||
|
# class: Chill\ReportBundle\Example
|
||||||
|
# arguments: [@service_id, "plain_value", %parameter%]
|
||||||
|
services:
|
||||||
|
|
||||||
|
chill.report.search:
|
||||||
|
class: Chill\ReportBundle\Search\ReportSearch
|
||||||
|
arguments:
|
||||||
|
- '@doctrine.orm.entity_manager'
|
||||||
|
- '@chill.main.security.authorization.helper'
|
||||||
|
- '@security.token_storage'
|
||||||
|
calls:
|
||||||
|
- [setContainer, ["@service_container"]]
|
||||||
|
tags:
|
||||||
|
- { name: chill.search, alias: 'report' }
|
||||||
|
|
||||||
|
chill.report.timeline:
|
||||||
|
class: Chill\ReportBundle\Timeline\TimelineReportProvider
|
||||||
|
arguments:
|
||||||
|
- '@doctrine.orm.entity_manager'
|
||||||
|
- '@chill.main.security.authorization.helper'
|
||||||
|
- '@security.token_storage'
|
||||||
|
- '@chill.custom_field.helper'
|
||||||
|
- '%chill_custom_fields.show_empty_values%'
|
||||||
|
tags:
|
||||||
|
- { name: chill.timeline, context: 'person' }
|
||||||
|
|
||||||
|
chill.report.security.authorization.report_voter:
|
||||||
|
class: Chill\ReportBundle\Security\Authorization\ReportVoter
|
||||||
|
arguments:
|
||||||
|
- "@chill.main.security.authorization.helper"
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
||||||
|
|
||||||
|
chill.report.form.report_type:
|
||||||
|
class: Chill\ReportBundle\Form\ReportType
|
||||||
|
arguments:
|
||||||
|
- "@chill.main.security.authorization.helper"
|
||||||
|
- "@security.token_storage"
|
||||||
|
- "@chill.main.helper.translatable_string"
|
||||||
|
- "@doctrine.orm.entity_manager"
|
||||||
|
tags:
|
||||||
|
- { name: form.type, alias: chill_reportbundle_report }
|
||||||
|
|
7
src/Bundle/ChillReport/config/services/controller.yaml
Normal file
7
src/Bundle/ChillReport/config/services/controller.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
Chill\ReportBundle\Controller\ReportController:
|
||||||
|
arguments:
|
||||||
|
$eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
|
||||||
|
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
|
||||||
|
$paginator: '@Chill\MainBundle\Pagination\PaginatorFactory'
|
||||||
|
tags: ['controller.service_arguments']
|
13
src/Bundle/ChillReport/config/services/export.yaml
Normal file
13
src/Bundle/ChillReport/config/services/export.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
Chill\ReportBundle\Export\Export\ReportListProvider:
|
||||||
|
arguments:
|
||||||
|
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||||
|
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||||
|
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||||
|
$customFieldProvider: '@Chill\CustomFieldsBundle\Service\CustomFieldProvider'
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_elements_provider, prefix: 'report' }
|
||||||
|
|
||||||
|
Chill\ReportBundle\Export\Filter\ReportDateFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'report_date' }
|
4
src/Bundle/ChillReport/config/services/fixtures.yaml
Normal file
4
src/Bundle/ChillReport/config/services/fixtures.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
services:
|
||||||
|
Chill\ReportBundle\DataFixtures\ORM\:
|
||||||
|
resource: ../../DataFixtures/ORM
|
||||||
|
tags: [ 'doctrine.fixture.orm' ]
|
5
src/Bundle/ChillReport/config/validation.yaml
Normal file
5
src/Bundle/ChillReport/config/validation.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Chill\ReportBundle\Entity\Report:
|
||||||
|
properties:
|
||||||
|
date:
|
||||||
|
- NotNull: ~
|
||||||
|
- Date: ~
|
1
src/Bundle/ChillReport/console.sh
Executable file
1
src/Bundle/ChillReport/console.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
php Tests/Fixtures/App/console $1 $2 $3 $4 $5
|
30
src/Bundle/ChillReport/migrations/Version20141129012050.php
Normal file
30
src/Bundle/ChillReport/migrations/Version20141129012050.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Report;
|
||||||
|
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
class Version20141129012050 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql("CREATE SEQUENCE Report_id_seq INCREMENT BY 1 MINVALUE 1 START 1;");
|
||||||
|
$this->addSql("CREATE TABLE Report (id INT NOT NULL, user_id INT DEFAULT NULL, person_id INT DEFAULT NULL, date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, scope VARCHAR(255) DEFAULT NULL, cFData JSON NOT NULL, cFGroup_id INT DEFAULT NULL, PRIMARY KEY(id));");
|
||||||
|
$this->addSql("CREATE INDEX IDX_C38372B2A76ED395 ON Report (user_id);");
|
||||||
|
$this->addSql("CREATE INDEX IDX_C38372B2217BBB47 ON Report (person_id);");
|
||||||
|
$this->addSql("CREATE INDEX IDX_C38372B216D2C9F0 ON Report (cFGroup_id);");
|
||||||
|
$this->addSql("ALTER TABLE Report ADD CONSTRAINT FK_C38372B2A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE;");
|
||||||
|
$this->addSql("ALTER TABLE Report ADD CONSTRAINT FK_C38372B2217BBB47 FOREIGN KEY (person_id) REFERENCES Person (id) NOT DEFERRABLE INITIALLY IMMEDIATE;");
|
||||||
|
$this->addSql("ALTER TABLE Report ADD CONSTRAINT FK_C38372B216D2C9F0 FOREIGN KEY (cFGroup_id) REFERENCES CustomFieldsGroup (id) NOT DEFERRABLE INITIALLY IMMEDIATE;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
116
src/Bundle/ChillReport/migrations/Version20150622233319.php
Normal file
116
src/Bundle/ChillReport/migrations/Version20150622233319.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Report;
|
||||||
|
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a scope to report
|
||||||
|
*/
|
||||||
|
class Version20150622233319 extends AbstractMigration
|
||||||
|
implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
public function setContainer(ContainerInterface $container = null)
|
||||||
|
{
|
||||||
|
if ($container === NULL) {
|
||||||
|
throw new \RuntimeException('Container is not provided. This migration '
|
||||||
|
. 'need container to set a default center');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->container = $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Schema $schema
|
||||||
|
*/
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql',
|
||||||
|
'Migration can only be executed safely on \'postgresql\'.');
|
||||||
|
|
||||||
|
$this->addSql('ALTER TABLE report ADD scope_id INT DEFAULT NULL');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before the upgrade to symfony version 4, this code worked.
|
||||||
|
*
|
||||||
|
* But it doesn't work any more after the migration and currently this
|
||||||
|
* code should note be necessary.
|
||||||
|
*
|
||||||
|
* This code is kept for reference, and may be re-activated if needed, but
|
||||||
|
* the probability that this will happens is near 0
|
||||||
|
*
|
||||||
|
|
||||||
|
//add a default scope
|
||||||
|
$scopes = $this->container->get('doctrine.orm.default_entity_manager')
|
||||||
|
->getRepository('ChillMainBundle:Scope')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
if (count($scopes) > 0) {
|
||||||
|
$defaultScopeId = $scopes[0]->getId();
|
||||||
|
} else {
|
||||||
|
//check if there are data in report table
|
||||||
|
$nbReports = $this->container->get('doctrine.orm.default_entity_manager')
|
||||||
|
->createQuery('SELECT count(r.id) FROM ChillReportBundle:Report r')
|
||||||
|
->getSingleScalarResult();
|
||||||
|
|
||||||
|
if ($nbReports > 0) {
|
||||||
|
//create a default scope
|
||||||
|
$scope = new Scope();
|
||||||
|
//create name according to installed languages
|
||||||
|
$locales = $this->container
|
||||||
|
->getParameter('chill_main.available_languages');
|
||||||
|
$names = array();
|
||||||
|
foreach($locales as $locale) {
|
||||||
|
$names[$locale] = 'default';
|
||||||
|
}
|
||||||
|
$scope->setName($names);
|
||||||
|
//persist
|
||||||
|
$this->container->get('doctrine.orm.default_entity_manager')
|
||||||
|
->persist($scope);
|
||||||
|
$this->container->get('doctrine.orm.default_entity_manager')
|
||||||
|
->flush();
|
||||||
|
//default scope is the newly-created one
|
||||||
|
$defaultScopeId = $scope->getId();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
$this->addSql('ALTER TABLE report DROP scope'); //before this migration, scope was never used
|
||||||
|
$this->addSql('ALTER TABLE report ADD CONSTRAINT FK_report_scope '
|
||||||
|
. 'FOREIGN KEY (scope_id) '
|
||||||
|
. 'REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
|
||||||
|
if (isset($defaultScopeId)){
|
||||||
|
$this->addSql('UPDATE report SET scope_id = :id', array(
|
||||||
|
'id' => $defaultScopeId
|
||||||
|
));
|
||||||
|
}
|
||||||
|
$this->addSql('ALTER TABLE report ALTER COLUMN scope_id SET NOT NULL');
|
||||||
|
$this->addSql('CREATE INDEX IDX_report_scope ON report (scope_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Schema $schema
|
||||||
|
*/
|
||||||
|
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 Report DROP CONSTRAINT FK_report_scope');
|
||||||
|
$this->addSql('DROP INDEX IDX_report_scope');
|
||||||
|
$this->addSql('ALTER TABLE Report ADD scope VARCHAR(255) DEFAULT NULL');
|
||||||
|
$this->addSql('ALTER TABLE Report DROP scope_id');
|
||||||
|
}
|
||||||
|
}
|
26
src/Bundle/ChillReport/phpunit.xml.dist
Normal file
26
src/Bundle/ChillReport/phpunit.xml.dist
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<phpunit bootstrap="./Resources/test/Fixtures/App/app/autoload.php" colors="true"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||||
|
backupGlobals="false">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="ChillMain test suite">
|
||||||
|
<directory suffix="Test.php">./Tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./</directory>
|
||||||
|
<exclude>
|
||||||
|
<directory>./Resources</directory>
|
||||||
|
<directory>./Tests</directory>
|
||||||
|
<directory>./vendor</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
<php>
|
||||||
|
<server name="KERNEL_DIR" value="./Resources/test/Fixtures/App/app/" />
|
||||||
|
<ini name="error_reporting" value="-16385"/>
|
||||||
|
</php>
|
||||||
|
</phpunit>
|
55
src/Bundle/ChillReport/translations/messages.fr.yml
Normal file
55
src/Bundle/ChillReport/translations/messages.fr.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'Report edit': "Edition d'un rapport"
|
||||||
|
'Save report': "Enregistrer le rapport"
|
||||||
|
'Reset report': "Remise à zéro"
|
||||||
|
'Add a report': "Ajout d'un rapport"
|
||||||
|
'Add report': 'Ajouter le rapport'
|
||||||
|
'Create a new report': 'Créer un nouveau rapport'
|
||||||
|
'Report view': "Détails d'un rapport"
|
||||||
|
'Update the report': 'Modifier le rapport'
|
||||||
|
'Report list': 'Liste des rapports'
|
||||||
|
Details: Détails
|
||||||
|
Person: Personne
|
||||||
|
Scope: Cercle
|
||||||
|
Date: Date
|
||||||
|
User: Utilisateur
|
||||||
|
'Report type': 'Type de rapport'
|
||||||
|
'View the report': "Voir le rapport"
|
||||||
|
Report data: Données du rapport
|
||||||
|
'Report view : %name%': 'Rapport : %name%'
|
||||||
|
Report: Rapport
|
||||||
|
No report registered for this person.: Aucun rapport pour cette personne.
|
||||||
|
|
||||||
|
#Flash messages
|
||||||
|
'Success : report created!': "Succès : le rapport a bien été créé !"
|
||||||
|
'The form is not valid. The report has not been created !': "Le formulaire comporte des erreurs, le rapport n'a pas été créé"
|
||||||
|
'Success : report updated!': "Succès : le rapport a bien été mis à jour !"
|
||||||
|
'The form is not valid. The report has not been updated !': "Le formulaire comporte des erreurs, le rapport n'a pas été mis à jour"
|
||||||
|
|
||||||
|
#Exception messags
|
||||||
|
'Unable to find this report.': Rapport introuvable.
|
||||||
|
'This is not the report of the person.': "La personne et le rapport sélectionnés ne sont pas associés"
|
||||||
|
|
||||||
|
|
||||||
|
#search
|
||||||
|
'You may not set a date argument and a date in default': Vous avez introduit deux dates, l'une avec l'argument date et l'autre en zone de recherche par défaut. Merci d'indiquer l'un ou l'autre
|
||||||
|
'You must provide either a date:YYYY-mm-dd argument or a YYYY-mm-dd default search': Merci d'indiquer soit un argument date:YYYY-mm-dd, soit une date dans la recherche par défaut.
|
||||||
|
'Reports search results': Recherche dans les rapports
|
||||||
|
'%total% reports matching the search "%pattern%"': '{0} Aucun rapport ne correspond à la recherche "%pattern%" | {1} Un rapport correspond à la recherche "%pattern%" | ]1,Inf]%total% rapports correspondent à la recherche "%pattern%"'
|
||||||
|
|
||||||
|
#timeline
|
||||||
|
'%user% has filled a %report_label% report': "%user% a ajouté un rapport '%report_label%'"
|
||||||
|
|
||||||
|
#roles
|
||||||
|
CHILL_REPORT_UPDATE: Modifier les rapports
|
||||||
|
CHILL_REPORT_SEE: Voir les rapports
|
||||||
|
CHILL_REPORT_CREATE: Créer des rapports
|
||||||
|
CHILL_REPORT_LISTS: Liste des rapports
|
||||||
|
|
||||||
|
|
||||||
|
#exports
|
||||||
|
"List for report '%type%'": Liste des rapports "%type%"
|
||||||
|
"Generate list of report '%type%'": Génère une liste des rapports "%type%"
|
||||||
|
"Report's question": Question du rapport
|
||||||
|
Filter by report's date: Filtrer par date de rapport
|
||||||
|
Report is after this date: Rapports après cette date
|
||||||
|
Report is before this date: Rapports avant cette date
|
31
src/Bundle/ChillReport/translations/messages.nl.yml
Normal file
31
src/Bundle/ChillReport/translations/messages.nl.yml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'Report edit': Dossier uitgave
|
||||||
|
'Save report': Bewaar consultatiegegevens
|
||||||
|
'Reset report': Verwijder consultatiegegevens
|
||||||
|
'Add a report': Voeg een consultatierapport toe
|
||||||
|
'Add report': 'Voeg rapport toe'
|
||||||
|
'Create a new report': 'Maak een nieuw consultatieblad aan'
|
||||||
|
'Report view': "Details van de consultatiegegevens"
|
||||||
|
'Update the report': 'Vul dossier aan'
|
||||||
|
'Report list': 'Overzicht van de consultaties'
|
||||||
|
Details: Details
|
||||||
|
Person: Person
|
||||||
|
Scope: Scope
|
||||||
|
Date: Datum
|
||||||
|
User: Gebruiker
|
||||||
|
'Report type': 'Soort of verslag'
|
||||||
|
'View the report': "Bekijk de consultatiegegevens"
|
||||||
|
|
||||||
|
#Flash messages
|
||||||
|
'Success : report created!': "De consultatiegegevens zijn correct ingevuld!"
|
||||||
|
'The form is not valid. The report has not been created !': "De gegevens zijn niet correct, er wordt geen rapport gemaakt"
|
||||||
|
'Success : report updated!': "Update is gelukt"
|
||||||
|
'The form is not valid. The report has not been updated !': "Update is mislukt, probeer opnieuw."
|
||||||
|
|
||||||
|
#Exception messags
|
||||||
|
'Unable to find this report.': Dossier is onvindbaar
|
||||||
|
'This is not the report of the person.': "Dit is niet het dossier van deze person"
|
||||||
|
|
||||||
|
'You are going to leave a page with unsubmitted data. Are you sure you want to leave ?': 'U bent aan te vertrekken een pagina die veranderdt datas behoudt. Bent u er zeker van dat u wil vertrekken ?'
|
||||||
|
|
||||||
|
#timeline
|
||||||
|
'%user% has filled a %report_label% report on %date%': "%user% heeft een rapport '%report_label%' toegevoegd om %date%"
|
Loading…
x
Reference in New Issue
Block a user