mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'master' into export
This commit is contained in:
commit
11439f96c2
@ -3,7 +3,7 @@ services:
|
||||
|
||||
before_script:
|
||||
- composer config github-oauth.github.com $GITHUB_TOKEN
|
||||
- composer install --no-interaction --ignore-platform-reqs
|
||||
- composer install --no-interaction
|
||||
- cp Tests/Fixtures/App/app/config/parameters.gitlab-ci.yml Tests/Fixtures/App/app/config/parameters.yml
|
||||
- php Tests/Fixtures/App/app/console.php --env=test cache:warmup
|
||||
- php Tests/Fixtures/App/app/console.php doctrine:migrations:migrate --env=test --no-interaction
|
||||
|
259
Controller/PersonAddressController.php
Normal file
259
Controller/PersonAddressController.php
Normal file
@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* Copyright (C) 2016, 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\PersonBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\MainBundle\Form\Type\AddressType;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Controller for addresses associated with person
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* @author Champs Libres <info@champs-libres.coop>
|
||||
*/
|
||||
class PersonAddressController extends Controller
|
||||
{
|
||||
|
||||
public function listAction($person_id)
|
||||
{
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository('ChillPersonBundle:Person')
|
||||
->find($person_id);
|
||||
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException("Person with id $person_id not"
|
||||
. " found ");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person,
|
||||
"You are not allowed to edit this person.");
|
||||
|
||||
return $this->render('ChillPersonBundle:Address:list.html.twig', array(
|
||||
'person' => $person
|
||||
));
|
||||
}
|
||||
|
||||
public function newAction($person_id)
|
||||
{
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository('ChillPersonBundle:Person')
|
||||
->find($person_id);
|
||||
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException("Person with id $person_id not"
|
||||
. " found ");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
"You are not allowed to edit this person.");
|
||||
|
||||
$address = new Address();
|
||||
|
||||
$form = $this->createCreateForm($person, $address);
|
||||
|
||||
return $this->render('ChillPersonBundle:Address:new.html.twig', array(
|
||||
'person' => $person,
|
||||
'form' => $form->createView()
|
||||
));
|
||||
}
|
||||
|
||||
public function createAction($person_id, Request $request)
|
||||
{
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository('ChillPersonBundle:Person')
|
||||
->find($person_id);
|
||||
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException("Person with id $person_id not"
|
||||
. " found ");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
"You are not allowed to edit this person.");
|
||||
|
||||
$address = new Address();
|
||||
|
||||
$form = $this->createCreateForm($person, $address);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$address = $form->getData();
|
||||
$person->addAddress($address);
|
||||
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success',
|
||||
$this->get('translator')->trans('The new address was created successfully')
|
||||
);
|
||||
|
||||
return $this->redirectToRoute('chill_person_address_list', array(
|
||||
'person_id' => $person->getId()
|
||||
));
|
||||
}
|
||||
|
||||
return $this->render('ChillPersonBundle:Address:new.html.twig', array(
|
||||
'person' => $person,
|
||||
'form' => $form->createView()
|
||||
));
|
||||
}
|
||||
|
||||
public function editAction($person_id, $address_id)
|
||||
{
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository('ChillPersonBundle:Person')
|
||||
->find($person_id);
|
||||
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException("Person with id $person_id not"
|
||||
. " found ");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
"You are not allowed to edit this person.");
|
||||
|
||||
$address = $this->findAddressById($person, $address_id);
|
||||
|
||||
$form = $this->createEditForm($person, $address);
|
||||
|
||||
return $this->render('ChillPersonBundle:Address:edit.html.twig', array(
|
||||
'person' => $person,
|
||||
'address' => $address,
|
||||
'form' => $form->createView()
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function updateAction($person_id, $address_id, Request $request)
|
||||
{
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository('ChillPersonBundle:Person')
|
||||
->find($person_id);
|
||||
|
||||
if ($person === NULL) {
|
||||
throw $this->createNotFoundException("Person with id $person_id not"
|
||||
. " found ");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
"You are not allowed to edit this person.");
|
||||
|
||||
$address = $this->findAddressById($person, $address_id);
|
||||
|
||||
$form = $this->createEditForm($person, $address);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->getDoctrine()->getManager()
|
||||
->flush();
|
||||
|
||||
$this->addFlash('success', $this->get('translator')->trans(
|
||||
"The address has been successfully updated"));
|
||||
|
||||
return $this->redirectToRoute('chill_person_address_list', array(
|
||||
'person_id' => $person->getId()
|
||||
));
|
||||
}
|
||||
|
||||
return $this->render('ChillPersonBundle:Address:edit.html.twig', array(
|
||||
'person' => $person,
|
||||
'address' => $address,
|
||||
'form' => $form->createView()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Person $person
|
||||
* @param Address $address
|
||||
* @return \Symfony\Component\Form\Form
|
||||
*/
|
||||
protected function createEditForm(Person $person, Address $address)
|
||||
{
|
||||
$form = $this->createForm(AddressType::class, $address, array(
|
||||
'method' => 'POST',
|
||||
'action' => $this->generateUrl('chill_person_address_update', array(
|
||||
'person_id' => $person->getId(),
|
||||
'address_id' => $address->getId()
|
||||
))
|
||||
));
|
||||
|
||||
$form->add('submit', 'submit', array(
|
||||
'label' => 'Submit'
|
||||
));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Person $person
|
||||
* @param Address $address
|
||||
* @return \Symfony\Component\Form\Form
|
||||
*/
|
||||
protected function createCreateForm(Person $person, Address $address)
|
||||
{
|
||||
$form = $this->createForm(AddressType::class, $address, array(
|
||||
'method' => 'POST',
|
||||
'action' => $this->generateUrl('chill_person_address_create', array(
|
||||
'person_id' => $person->getId()
|
||||
))
|
||||
));
|
||||
|
||||
$form->add('submit', 'submit', array(
|
||||
'label' => 'Submit'
|
||||
));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Person $person
|
||||
* @param int $address_id
|
||||
* @return Address
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the address id does not exists or is not associated with given person
|
||||
*/
|
||||
protected function findAddressById(Person $person, $address_id)
|
||||
{
|
||||
// filtering address
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria::expr()->eq('id', $address_id))
|
||||
->setMaxResults(1);
|
||||
$addresses = $person->getAddresses()->matching($criteria);
|
||||
|
||||
if (count($addresses) === 0) {
|
||||
throw $this->createNotFoundException("Address with id $address_id "
|
||||
. "matching person $person_id not found ");
|
||||
}
|
||||
|
||||
return $addresses->first();
|
||||
}
|
||||
}
|
@ -75,7 +75,7 @@ class PersonController extends Controller
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
'You are not allowed to edit this person');
|
||||
|
||||
$form = $this->createForm(new PersonType(), $person,
|
||||
$form = $this->createForm(PersonType::class, $person,
|
||||
array(
|
||||
"action" => $this->generateUrl('chill_person_general_update',
|
||||
array("person_id" => $person_id)),
|
||||
@ -98,7 +98,7 @@ class PersonController extends Controller
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
'You are not allowed to edit this person');
|
||||
|
||||
$form = $this->createForm(new PersonType(), $person,
|
||||
$form = $this->createForm(PersonType::class, $person,
|
||||
array("cFGroup" => $this->getCFGroup()));
|
||||
|
||||
if ($request->getMethod() === 'POST') {
|
||||
@ -302,7 +302,9 @@ class PersonController extends Controller
|
||||
|
||||
$dql = 'SELECT p from ChillPersonBundle:Person p WHERE '
|
||||
. 'LOWER(p.firstName) LIKE LOWER(:firstName)'
|
||||
. ' OR LOWER(p.lastName) LIKE LOWER(:lastName)';
|
||||
. ' OR LOWER(p.lastName) LIKE LOWER(:lastName)'
|
||||
. ' OR LOWER(p.firstName) LIKE LOWER(:lastName)'
|
||||
. ' OR LOWER(p.lastName) LIKE LOWER(:firstName)';
|
||||
|
||||
$query->setParameter('firstName', $form['firstName']->getData())
|
||||
->setParameter('lastName', $form['lastName']->getData());
|
||||
|
@ -26,6 +26,8 @@ use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
|
||||
/**
|
||||
* Load people into database
|
||||
@ -38,6 +40,13 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
|
||||
|
||||
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
protected $faker;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->faker = \Faker\Factory::create('fr_FR');
|
||||
}
|
||||
|
||||
public function prepare()
|
||||
{
|
||||
//prepare days, month, years
|
||||
@ -114,12 +123,27 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
|
||||
$firstName = $this->firstNamesFemale[array_rand($this->firstNamesFemale)];
|
||||
}
|
||||
|
||||
// add an address on 80% of the created people
|
||||
if (rand(0,100) < 80) {
|
||||
$address = $this->getRandomAddress();
|
||||
// on 30% of those person, add multiple addresses
|
||||
if (rand(0,10) < 4) {
|
||||
$address = array(
|
||||
$address,
|
||||
$this->getRandomAddress()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$address = null;
|
||||
}
|
||||
|
||||
$person = array(
|
||||
'FirstName' => $firstName,
|
||||
'LastName' => $lastName,
|
||||
'Gender' => $sex,
|
||||
'Nationality' => (rand(0,100) > 50) ? NULL: 'BE',
|
||||
'center' => (rand(0,1) == 0) ? 'centerA': 'centerB',
|
||||
'Address' => $address,
|
||||
'maritalStatus' => $this->maritalStatusRef[array_rand($this->maritalStatusRef)]
|
||||
);
|
||||
|
||||
@ -142,10 +166,18 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
|
||||
'Email' => "Email d'un ami: roger@tt.com",
|
||||
'CountryOfBirth' => 'BE',
|
||||
'Nationality' => 'BE',
|
||||
'CFData' => array()
|
||||
'CFData' => array(),
|
||||
'Address' => null
|
||||
), $specific);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new person from array data
|
||||
*
|
||||
* @param array $person
|
||||
* @param ObjectManager $manager
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function addAPerson(array $person, ObjectManager $manager)
|
||||
{
|
||||
$p = new Person();
|
||||
@ -164,13 +196,51 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
|
||||
$value = $this->getReference($value);
|
||||
break;
|
||||
}
|
||||
call_user_func(array($p, 'set'.$key), $value);
|
||||
|
||||
//try to add the data using the setSomething function,
|
||||
// if not possible, fallback to addSomething function
|
||||
if (method_exists($p, 'set'.$key)) {
|
||||
call_user_func(array($p, 'set'.$key), $value);
|
||||
} elseif (method_exists($p, 'add'.$key)) {
|
||||
// if we have a "addSomething", we may have multiple items to add
|
||||
// so, we set the value in an array if it is not an array, and
|
||||
// will call the function addSomething multiple times
|
||||
if (!is_array($value)) {
|
||||
$value = array($value);
|
||||
}
|
||||
|
||||
foreach($value as $v) {
|
||||
if ($v !== NULL) {
|
||||
call_user_func(array($p, 'add'.$key), $v);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$manager->persist($p);
|
||||
echo "add person'".$p->__toString()."'\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creata a random address
|
||||
*
|
||||
* @return Address
|
||||
*/
|
||||
private function getRandomAddress()
|
||||
{
|
||||
return (new Address())
|
||||
->setStreetAddress1($this->faker->streetAddress)
|
||||
->setStreetAddress2(
|
||||
rand(0,9) > 5 ? $this->faker->streetAddress : ''
|
||||
)
|
||||
->setPostcode($this->getReference(
|
||||
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
|
||||
))
|
||||
->setValidFrom($this->faker->dateTimeBetween('-5 years'))
|
||||
;
|
||||
}
|
||||
|
||||
private function getCountry($countryCode)
|
||||
{
|
||||
if ($countryCode === NULL) {
|
||||
|
@ -32,10 +32,25 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
||||
// set configuration for validation
|
||||
$container->setParameter('chill_person.validation.birtdate_not_before',
|
||||
$config['validation']['birthdate_not_after']);
|
||||
|
||||
$this->handlePersonFieldsParameters($container, $config['person_fields']);
|
||||
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('services.yml');
|
||||
}
|
||||
|
||||
private function handlePersonFieldsParameters(ContainerBuilder $container, $config)
|
||||
{
|
||||
if (array_key_exists('enabled', $config)) {
|
||||
unset($config['enabled']);
|
||||
}
|
||||
|
||||
$container->setParameter('chill_person.person_fields', $config);
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
$container->setParameter('chill_person.person_fields.'.$key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
private function declarePersonAsCustomizable (ContainerBuilder $container)
|
||||
{
|
||||
@ -67,6 +82,18 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
||||
$asseticConfig['bundles'][] = 'ChillPersonBundle';
|
||||
$container->prependExtensionConfig('assetic',
|
||||
array('bundles' => array('ChillPersonBundle')));
|
||||
|
||||
//add person_fields parameter as global
|
||||
$chillPersonConfig = $container->getExtensionConfig($this->getAlias());
|
||||
$config = $this->processConfiguration(new Configuration(), $chillPersonConfig);
|
||||
$twigConfig = array(
|
||||
'globals' => array(
|
||||
'chill_person' => array(
|
||||
'fields' => $config['person_fields']
|
||||
)
|
||||
)
|
||||
);
|
||||
$container->prependExtensionConfig('twig', $twigConfig);
|
||||
|
||||
$this-> declarePersonAsCustomizable($container);
|
||||
|
||||
|
@ -27,40 +27,71 @@ class Configuration implements ConfigurationInterface
|
||||
$rootNode
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
->arrayNode('search')
|
||||
->canBeDisabled()
|
||||
->arrayNode('search')
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
->booleanNode('use_double_metaphone')
|
||||
->defaultFalse()
|
||||
->end()
|
||||
->booleanNode('use_trigrams')
|
||||
->defaultFalse()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('validation')
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
->scalarNode('birthdate_not_after')
|
||||
->info($this->validationBirthdateNotAfterInfos)
|
||||
->defaultValue('P1D')
|
||||
->validate()
|
||||
->ifTrue(function($period) {
|
||||
try {
|
||||
$interval = new \DateInterval($period);
|
||||
} catch (\Exception $ex) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
->thenInvalid('Invalid period for birthdate validation : "%s" '
|
||||
. 'The parameter should match duration as defined by ISO8601 : '
|
||||
. 'https://en.wikipedia.org/wiki/ISO_8601#Durations')
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
->booleanNode('use_double_metaphone')
|
||||
->defaultFalse()
|
||||
->end() // use_double_metaphone, parent = children for 'search'
|
||||
->booleanNode('use_trigrams')
|
||||
->defaultFalse()
|
||||
->end() // use_trigrams, parent = children of 'search'
|
||||
->end() //children for 'search', parent = array node 'search'
|
||||
->end() // array 'search', parent = children of root
|
||||
->arrayNode('validation')
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
->scalarNode('birthdate_not_after')
|
||||
->info($this->validationBirthdateNotAfterInfos)
|
||||
->defaultValue('P1D')
|
||||
->validate()
|
||||
->ifTrue(function($period) {
|
||||
try {
|
||||
$interval = new \DateInterval($period);
|
||||
} catch (\Exception $ex) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
->thenInvalid('Invalid period for birthdate validation : "%s" '
|
||||
. 'The parameter should match duration as defined by ISO8601 : '
|
||||
. 'https://en.wikipedia.org/wiki/ISO_8601#Durations')
|
||||
->end() // birthdate_not_after, parent = children of validation
|
||||
|
||||
->end() // children for 'validation', parent = validation
|
||||
->end() //validation, parent = children of root
|
||||
->end() // children of root, parent = root
|
||||
->arrayNode('person_fields')
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
->append($this->addFieldNode('place_of_birth'))
|
||||
->append($this->addFieldNode('email'))
|
||||
->append($this->addFieldNode('phonenumber'))
|
||||
->append($this->addFieldNode('nationality'))
|
||||
->append($this->addFieldNode('country_of_birth'))
|
||||
->append($this->addFieldNode('marital_status'))
|
||||
->append($this->addFieldNode('spoken_languages'))
|
||||
->append($this->addFieldNode('address'))
|
||||
->end() //children for 'person_fields', parent = array 'person_fields'
|
||||
->end() // person_fields, parent = children of root
|
||||
->end() // children of 'root', parent = root
|
||||
;
|
||||
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
|
||||
private function addFieldNode($key)
|
||||
{
|
||||
$tree = new TreeBuilder();
|
||||
$node = $tree->root($key, 'enum');
|
||||
|
||||
$node
|
||||
->values(array('hidden', 'visible'))
|
||||
->defaultValue('visible')
|
||||
->info("If the field $key must be shown")
|
||||
->end();
|
||||
//var_dump($node);
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ use Chill\MainBundle\Entity\Country;
|
||||
use Chill\PersonBundle\Entity\MaritalStatus;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
|
||||
/**
|
||||
* Person
|
||||
@ -100,9 +102,16 @@ class Person implements HasCenterInterface {
|
||||
/** @var array Array where customfield's data are stored */
|
||||
private $cFData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
private $addresses;
|
||||
|
||||
public function __construct(\DateTime $opening = null) {
|
||||
$this->accompanyingPeriods = new ArrayCollection();
|
||||
$this->spokenLanguages = new ArrayCollection();
|
||||
$this->addresses = new ArrayCollection();
|
||||
|
||||
if ($opening === null) {
|
||||
$opening = new \DateTime();
|
||||
@ -597,6 +606,45 @@ class Person implements HasCenterInterface {
|
||||
return $this->spokenLanguages;
|
||||
}
|
||||
|
||||
public function addAddress(Address $address)
|
||||
{
|
||||
$this->addresses[] = $address;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAddress(Address $address)
|
||||
{
|
||||
$this->addresses->removeElement($address);
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, the addresses are ordered by date, descending (the most
|
||||
* recent first)
|
||||
*
|
||||
* @return \Chill\MainBundle\Entity\Address[]
|
||||
*/
|
||||
public function getAddresses()
|
||||
{
|
||||
return $this->addresses;
|
||||
}
|
||||
|
||||
public function getLastAddress(\DateTime $date = null)
|
||||
{
|
||||
if ($date === null) {
|
||||
$date = new \DateTime('now');
|
||||
}
|
||||
|
||||
$addresses = $this->getAddresses();
|
||||
|
||||
if ($addresses == null) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $addresses->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback that checks if the accompanying periods are valid
|
||||
*
|
||||
|
@ -28,6 +28,25 @@ use Chill\PersonBundle\Form\Type\GenderType;
|
||||
|
||||
class PersonType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* array of configuration for person_fields.
|
||||
*
|
||||
* Contains whether we should add fields some optional fields (optional per
|
||||
* instance)
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string[] $personFieldsConfiguration configuration of visibility of some fields
|
||||
*/
|
||||
public function __construct(array $personFieldsConfiguration)
|
||||
{
|
||||
$this->config = $personFieldsConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param array $options
|
||||
@ -38,27 +57,48 @@ class PersonType extends AbstractType
|
||||
->add('firstName')
|
||||
->add('lastName')
|
||||
->add('birthdate', 'date', array('required' => false, 'widget' => 'single_text', 'format' => 'dd-MM-yyyy'))
|
||||
->add('placeOfBirth', 'text', array('required' => false))
|
||||
->add('gender', new GenderType(), array(
|
||||
'required' => true
|
||||
))
|
||||
->add('memo', 'textarea', array('required' => false))
|
||||
->add('phonenumber', 'textarea', array('required' => false))
|
||||
->add('email', 'textarea', array('required' => false))
|
||||
->add('countryOfBirth', 'select2_chill_country', array(
|
||||
;
|
||||
|
||||
if ($this->config['place_of_birth'] === 'visible') {
|
||||
$builder->add('placeOfBirth', 'text', array('required' => false));
|
||||
}
|
||||
|
||||
if ($this->config['phonenumber'] === 'visible') {
|
||||
$builder->add('phonenumber', 'textarea', array('required' => false));
|
||||
}
|
||||
|
||||
if ($this->config['email'] === 'visible') {
|
||||
$builder->add('email', 'textarea', array('required' => false));
|
||||
}
|
||||
|
||||
if ($this->config['country_of_birth'] === 'visible') {
|
||||
$builder->add('countryOfBirth', 'select2_chill_country', array(
|
||||
'required' => false
|
||||
))
|
||||
->add('nationality', 'select2_chill_country', array(
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->config['nationality'] === 'visible') {
|
||||
$builder->add('nationality', 'select2_chill_country', array(
|
||||
'required' => false
|
||||
))
|
||||
->add('spokenLanguages', 'select2_chill_language', array(
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->config['spoken_languages'] === 'visible') {
|
||||
$builder->add('spokenLanguages', 'select2_chill_language', array(
|
||||
'required' => false,
|
||||
'multiple' => true
|
||||
))
|
||||
->add('maritalStatus', 'select2_chill_marital_status', array(
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->config['marital_status'] === 'visible'){
|
||||
$builder->add('maritalStatus', 'select2_chill_marital_status', array(
|
||||
'required' => false
|
||||
))
|
||||
;
|
||||
));
|
||||
}
|
||||
|
||||
if($options['cFGroup']) {
|
||||
$builder
|
||||
|
161
Form/Type/PickPersonType.php
Normal file
161
Form/Type/PickPersonType.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 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\PersonBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\PersonBundle\Entity\PersonRepository;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\MainBundle\Entity\GroupCenter;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
/**
|
||||
* This type allow to pick a person.
|
||||
*
|
||||
* The form is embedded in a select2 input.
|
||||
*
|
||||
* The people may be filtered :
|
||||
*
|
||||
* - with the `centers` option, only the people associated with the given center(s)
|
||||
* are seen. May be an instance of `Chill\MainBundle\Entity\Center`, or an array of
|
||||
* `Chill\MainBundle\Entity\Center`. By default, all the reachable centers as selected.
|
||||
* - with the `role` option, only the people belonging to the reachable center for the
|
||||
* given role are displayed.
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class PickPersonType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var PersonRepository
|
||||
*/
|
||||
protected $personRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Chill\MainBundle\Entity\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
public function __construct(
|
||||
PersonRepository $personRepository,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
AuthorizationHelper $authorizationHelper
|
||||
)
|
||||
{
|
||||
$this->personRepository = $personRepository;
|
||||
$this->user = $tokenStorage->getToken()->getUser();
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$qb = $options['query_builder'];
|
||||
|
||||
if ($options['role'] === NULL) {
|
||||
$centers = array_map(function (GroupCenter $g) {
|
||||
|
||||
return $g->getCenter();
|
||||
}, $this->user->getGroupCenters()->toArray());
|
||||
} else {
|
||||
$centers = $this->authorizationHelper
|
||||
->getReachableCenters($this->user, $options['role']);
|
||||
}
|
||||
|
||||
if ($options['centers'] === NULL) {
|
||||
// we select all selected centers
|
||||
$selectedCenters = $centers;
|
||||
} else {
|
||||
$selectedCenters = array();
|
||||
$options['centers'] = is_array($options['centers']) ?
|
||||
$options['centers'] : array($options['centers']);
|
||||
|
||||
foreach ($options['centers'] as $c) {
|
||||
// check that every member of the array is a center
|
||||
if (!$c instanceof Center) {
|
||||
throw new \RuntimeException('Every member of the "centers" '
|
||||
. 'option must be an instance of '.Center::class);
|
||||
}
|
||||
if (!in_array($c->getId(), array_map(
|
||||
function(Center $c) { return $c->getId();},
|
||||
$centers))) {
|
||||
throw new AccessDeniedException('The given center is not reachable');
|
||||
}
|
||||
$selectedCenters[] = $c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$qb
|
||||
->orderBy('p.firstName', 'ASC')
|
||||
->orderBy('p.lastName', 'ASC')
|
||||
->where($qb->expr()->in('p.center', ':centers'))
|
||||
->setParameter('centers', $selectedCenters)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
// add the possibles options for this type
|
||||
$resolver->setDefined('centers')
|
||||
->addAllowedTypes('centers', array('array', Center::class, 'null'))
|
||||
->setDefault('centers', null)
|
||||
->setDefined('role')
|
||||
->addAllowedTypes('role', array(Role::class, 'null'))
|
||||
->setDefault('role', null)
|
||||
;
|
||||
|
||||
// add the default options
|
||||
$resolver->setDefaults(array(
|
||||
'class' => Person::class,
|
||||
'choice_label' => function(Person $p) {
|
||||
return $p->getFirstname().' '.$p->getLastname();
|
||||
},
|
||||
'placeholder' => 'Pick a person',
|
||||
'choice_attr' => function(Person $p) {
|
||||
return array(
|
||||
'data-center' => $p->getCenter()->getId()
|
||||
);
|
||||
},
|
||||
'attr' => array('class' => 'select2 '),
|
||||
'query_builder' => $this->personRepository->createQueryBuilder('p')
|
||||
));
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return \Symfony\Bridge\Doctrine\Form\Type\EntityType::class;
|
||||
}
|
||||
|
||||
}
|
@ -72,4 +72,10 @@ Chill\PersonBundle\Entity\Person:
|
||||
inverseJoinColumns:
|
||||
language_id:
|
||||
referencedColumnName: id
|
||||
addresses:
|
||||
targetEntity: Chill\MainBundle\Entity\Address
|
||||
orderBy: { 'validFrom': 'DESC' }
|
||||
joinTable:
|
||||
name: chill_person_persons_to_addresses
|
||||
cascade: [persist, remove, merge, detach]
|
||||
lifecycleCallbacks: { }
|
||||
|
@ -69,6 +69,27 @@ chill_person_accompanying_period_close:
|
||||
chill_person_accompanying_period_open:
|
||||
path: /{_locale}/person/{person_id}/accompanying-period/open
|
||||
defaults: { _controller: ChillPersonBundle:AccompanyingPeriod:open }
|
||||
|
||||
chill_person_address_list:
|
||||
path: /{_locale}/person/{person_id}/address/list
|
||||
defaults: { _controller: ChillPersonBundle:PersonAddress:list }
|
||||
|
||||
chill_person_address_create:
|
||||
path: /{_locale}/person/{person_id}/address/create
|
||||
defaults: { _controller: ChillPersonBundle:PersonAddress:create }
|
||||
methods: [POST]
|
||||
|
||||
chill_person_address_new:
|
||||
path: /{_locale}/person/{person_id}/address/new
|
||||
defaults: { _controller: ChillPersonBundle:PersonAddress:new }
|
||||
|
||||
chill_person_address_edit:
|
||||
path: /{_locale}/person/{person_id}/address/{address_id}/edit
|
||||
defaults: { _controller: ChillPersonBundle:PersonAddress:edit }
|
||||
|
||||
chill_person_address_update:
|
||||
path: /{_locale}/person/{person_id}/address/{address_id}/update
|
||||
defaults: { _controller: ChillPersonBundle:PersonAddress:update }
|
||||
|
||||
chill_person_export:
|
||||
path: /{_locale}/person/export/
|
||||
|
@ -1,7 +1,14 @@
|
||||
parameters:
|
||||
# cl_chill_person.example.class: Chill\PersonBundle\Example
|
||||
|
||||
services:
|
||||
services:
|
||||
chill.person.form.person_creation:
|
||||
class: Chill\PersonBundle\Form\PersonType
|
||||
arguments:
|
||||
- %chill_person.person_fields%
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
chill.person.accompanying_period_closing_motive:
|
||||
class: Chill\PersonBundle\Form\Type\ClosingMotiveType
|
||||
scope: request
|
||||
@ -93,4 +100,19 @@ services:
|
||||
arguments:
|
||||
- "@translator"
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: person_gender_aggregator }
|
||||
- { name: chill.export_aggregator, alias: person_gender_aggregator }
|
||||
|
||||
chill.person.form.type.pick_person:
|
||||
class: Chill\PersonBundle\Form\Type\PickPersonType
|
||||
arguments:
|
||||
- "@chill.person.repository.person"
|
||||
- "@security.token_storage"
|
||||
- "@chill.main.security.authorization.helper"
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
chill.person.repository.person:
|
||||
class: Chill\PersonBundle\Entity\PersonRepository
|
||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
||||
arguments:
|
||||
- 'Chill\PersonBundle\Entity\Person'
|
||||
|
49
Resources/migrations/Version20160310161006.php
Normal file
49
Resources/migrations/Version20160310161006.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Application\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Add a many-to-many relationship between person and addresses
|
||||
*/
|
||||
class Version20160310161006 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('CREATE TABLE chill_person_persons_to_addresses ('
|
||||
. 'person_id INT NOT NULL, '
|
||||
. 'address_id INT NOT NULL, '
|
||||
. 'PRIMARY KEY(person_id, address_id))');
|
||||
$this->addSql('CREATE INDEX IDX_4655A196217BBB47 '
|
||||
. 'ON chill_person_persons_to_addresses (person_id)');
|
||||
$this->addSql('CREATE INDEX IDX_4655A196F5B7AF75 '
|
||||
. 'ON chill_person_persons_to_addresses (address_id)');
|
||||
$this->addSql('ALTER TABLE chill_person_persons_to_addresses '
|
||||
. 'ADD CONSTRAINT FK_4655A196217BBB47 '
|
||||
. 'FOREIGN KEY (person_id) '
|
||||
. 'REFERENCES Person (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_person_persons_to_addresses '
|
||||
. 'ADD CONSTRAINT FK_4655A196F5B7AF75 '
|
||||
. 'FOREIGN KEY (address_id) '
|
||||
. 'REFERENCES chill_main_address (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP TABLE chill_person_persons_to_addresses');
|
||||
|
||||
}
|
||||
}
|
@ -68,8 +68,24 @@ Reset: 'Remise à zéro'
|
||||
'Create accompanying period': 'Nouvelle ouverture-fermeture à une autre date'
|
||||
'Closing motive': 'Motif de clôture'
|
||||
'Person details': 'Détails de la personne'
|
||||
'Update details for %name%': 'Modifier détails de %name%'
|
||||
Accompanying period list: Périodes d'accompagnement
|
||||
|
||||
# pickAPersonType
|
||||
Pick a person: Choisir une personne
|
||||
|
||||
#address
|
||||
Since %date%: Depuis le %date%
|
||||
No address given: Pas d'adresse renseignée
|
||||
The address has been successfully updated: L'adresse a été mise à jour avec succès
|
||||
Update address for %name%: Mettre à jour une adresse pour %name%
|
||||
Addresses'history for %name%: Historique des adresses de %name%
|
||||
Addresses'history: Historique des adresses
|
||||
New address for %name% : Nouvelle adresse pour %name%
|
||||
The new address was created successfully: La nouvelle adresse a été créée
|
||||
Add an address: Ajouter une adresse
|
||||
Back to the person details: Retour aux détails de la personne
|
||||
|
||||
#timeline
|
||||
'An accompanying period is opened for %person% on %date%': Une période d'accompagnement a été ouverte le %date% pour %person%
|
||||
'An accompanying period is closed for %person% on %date%': Une période d'accompagnement a été fermée le %date% pour %person%
|
||||
|
47
Resources/views/Address/edit.html.twig
Normal file
47
Resources/views/Address/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 "ChillPersonBundle::layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title 'Update address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) %}
|
||||
|
||||
{% block personcontent %}
|
||||
|
||||
<h1>{{ 'Update address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.streetAddress1) }}
|
||||
{{ form_row(form.streetAddress2) }}
|
||||
{{ form_row(form.postCode) }}
|
||||
{{ form_row(form.validFrom) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_person_address_list', { 'person_id' : person.id } ) }}" class="sc-button btn-cancel">
|
||||
{{ 'Back to the list'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ form_row(form.submit, { 'attr' : { 'class': 'sc-button bt-edit' } } ) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock personcontent %}
|
84
Resources/views/Address/list.html.twig
Normal file
84
Resources/views/Address/list.html.twig
Normal file
@ -0,0 +1,84 @@
|
||||
{#
|
||||
* 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 "ChillPersonBundle::layout.html.twig" %}
|
||||
|
||||
{% import 'ChillMainBundle:Address:macro.html.twig' as address_macros %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}{{ 'Addresses\'history for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
|
||||
<h1>{{ 'Addresses\'history for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1>
|
||||
|
||||
<table class="records_list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'Valid from'|trans }}</th>
|
||||
<th>{{ 'Address'|trans }}</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if person.addresses|length == 0 %}
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<span class="chill-no-data-statement">{{ 'No address given'|trans }}</span>
|
||||
<a href="{{ path('chill_person_address_new', { 'person_id' : person.id } ) }}">
|
||||
{{ 'Add an address'|trans }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
{% for address in person.addresses %}
|
||||
<tr>
|
||||
<td><strong>{{ 'Since %date%'|trans( { '%date%' : address.validFrom|localizeddate('long', 'none') } ) }}</strong></td>
|
||||
|
||||
<td>
|
||||
{{ address_macros._render(address, { 'with_valid_from' : false } ) }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_person_address_edit', { 'person_id': person.id, 'address_id' : address.id } ) }}" class="sc-button bt-edit">
|
||||
{{ 'Edit'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_person_view', { 'person_id' : person.id } ) }}" class="sc-button btn-cancel">
|
||||
{{ 'Back to the person details'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('chill_person_address_new', { 'person_id' : person.id } ) }}" class="sc-button btn-create">
|
||||
{{ 'Add an address'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% endblock personcontent %}
|
47
Resources/views/Address/new.html.twig
Normal file
47
Resources/views/Address/new.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 "ChillPersonBundle::layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}{{ 'New address for %name%'|trans({ '%name%': person.firstName|capitalize ~ ' ' ~ person.lastName } )|capitalize }}{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
|
||||
<h1>{{ 'New address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.streetAddress1) }}
|
||||
{{ form_row(form.streetAddress2) }}
|
||||
{{ form_row(form.postCode) }}
|
||||
{{ form_row(form.validFrom) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_person_address_list', { 'person_id' : person.id } ) }}" class="sc-button btn-cancel">
|
||||
{{ 'Back to the list'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ form_row(form.submit, { 'attr' : { 'class': 'sc-button bt-create' } } ) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock personcontent %}
|
@ -18,14 +18,21 @@
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}ChillPersonBundle:Person:see{% endblock %}
|
||||
{% block title %}{{ 'Update details for %name%'|trans({ '%name%': person.firstName|capitalize ~ ' ' ~ person.lastName } )|capitalize }}{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
|
||||
<h1>{{ 'Update details for %name%'|trans({ '%name%': person.firstName|capitalize ~ ' ' ~ person.lastName|capitalize } ) }}</h1>
|
||||
|
||||
{% form_theme form 'ChillMainBundle:Form:fields.html.twig' %}
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'Memo'|trans }}</h2></legend>
|
||||
{{ form_row(form.memo, {'label' : 'Memo'} ) }}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'General information'|trans }}</h2></legend>
|
||||
{{ form_row(form.firstName, {'label' : 'First name'}) }}
|
||||
@ -36,25 +43,41 @@
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'Birth information'|trans }}</h2></legend>
|
||||
{{ form_row(form.birthdate, {'label': 'Date of birth'} ) }}
|
||||
{%- if form.placeOfBirth is defined -%}
|
||||
{{ form_row(form.placeOfBirth, { 'label' : 'Place of birth'} ) }}
|
||||
{%- endif -%}
|
||||
{%- if form.countryOfBirth is defined -%}
|
||||
{{ form_row(form.countryOfBirth, { 'label' : 'Country of birth' } ) }}
|
||||
{%- endif -%}
|
||||
</fieldset>
|
||||
|
||||
{%- if form.nationality is defined or form.spokenLanguages is defined or form.maritalStatus is defined -%}
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'Administrative information'|trans }}</h2></legend>
|
||||
{%- if form.nationality is defined -%}
|
||||
{{ form_row(form.nationality, { 'label' : 'Nationality'|trans} ) }}
|
||||
{%- endif -%}
|
||||
{%- if form.spokenLanguages is defined -%}
|
||||
{{ form_row(form.spokenLanguages, {'label' : 'Spoken languages'}) }}
|
||||
{%- endif -%}
|
||||
{%- if form.maritalStatus is defined -%}
|
||||
{{ form_row(form.maritalStatus, { 'label' : 'Marital status'} ) }}
|
||||
{%- endif -%}
|
||||
</fieldset>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if form.email is defined or form.phonenumber is defined -%}
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'Contact information'|trans }}</h2></legend>
|
||||
{%- if form.email is defined -%}
|
||||
{{ form_row(form.email, {'label': 'Email'}) }}
|
||||
{%- endif -%}
|
||||
{%- if form.phonenumber is defined -%}
|
||||
{{ form_row(form.phonenumber, {'label': 'Phonenumber'}) }}
|
||||
|
||||
{%- endif -%}
|
||||
</fieldset>
|
||||
{%- endif -%}
|
||||
|
||||
{{ form_row(form.memo, {'label' : 'Memo'} ) }}
|
||||
|
||||
{{ form_rest(form) }}
|
||||
|
||||
|
1
Resources/views/Person/macro.html.twig
Normal file
1
Resources/views/Person/macro.html.twig
Normal file
@ -0,0 +1 @@
|
||||
{% macro render(p, withLink=false) %}<span class="entity entity-person person-person"><a href="{{ path('chill_person_view', { 'person_id' : p.id } ) }}">{{ p.firstName }} {{ p.lastName }}</a></span>{% endmacro %}
|
@ -16,6 +16,8 @@
|
||||
#}
|
||||
{% extends "ChillPersonBundle::layout.html.twig" %}
|
||||
|
||||
{% import 'ChillMainBundle:Address:macro.html.twig' as address %}
|
||||
|
||||
{% set activeRouteKey = 'chill_person_view' %}
|
||||
|
||||
{#
|
||||
@ -23,7 +25,8 @@ This view should receive those arguments:
|
||||
- person
|
||||
#}
|
||||
|
||||
{% block title %}ChillPersonBundle:Person:see{% endblock %}
|
||||
{% block title %}{{ 'Person details'|trans|capitalize ~ ' ' ~ person.firstName|capitalize ~
|
||||
' ' ~ person.lastName }}{% endblock %}
|
||||
{#
|
||||
we define variables to include an edit form repeated multiple time across
|
||||
the page
|
||||
@ -34,8 +37,21 @@ This view should receive those arguments:
|
||||
|
||||
|
||||
{% block personcontent %}
|
||||
|
||||
|
||||
|
||||
<div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent">
|
||||
{% if person.memo is not empty %}
|
||||
<div class="grid-12">
|
||||
<figure class="person-details">
|
||||
<h2 class="chill-red">{{ 'Memo'|trans|upper }}</h2>
|
||||
|
||||
<p>
|
||||
<blockquote>{{ person.memo|nl2br }}</blockquote>
|
||||
</p>
|
||||
</figure>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="grid-6">
|
||||
<figure class="person-details">
|
||||
<h2 class="chill-red">{{ 'General information'|trans|upper }}</h2>
|
||||
@ -72,8 +88,12 @@ This view should receive those arguments:
|
||||
{%- endif -%}
|
||||
</dd>
|
||||
|
||||
{%- if chill_person.fields.place_of_birth == 'visible' -%}
|
||||
<dt>{{ 'Place of birth'|trans }} :</dt>
|
||||
<dd>{{ person.placeOfBirth }}</dd>
|
||||
{%- endif -%}
|
||||
{%- if chill_person.fields.country_of_birth == 'visible' -%}
|
||||
<dt>{{ 'Country of birth'|trans }} :</dt>
|
||||
<dd>{% spaceless %}
|
||||
{% if person.countryOfBirth is not null %}
|
||||
{{ person.countryOfBirth.name|localize_translatable_string }}
|
||||
@ -81,6 +101,7 @@ This view should receive those arguments:
|
||||
{{ 'Unknown country of birth'|trans }}
|
||||
{% endif %}
|
||||
{% endspaceless %}</dd>
|
||||
{%- endif -%}
|
||||
</dl>
|
||||
|
||||
{% if is_granted('CHILL_PERSON_UPDATE', person) %}
|
||||
@ -91,10 +112,12 @@ This view should receive those arguments:
|
||||
</div>
|
||||
|
||||
<div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent">
|
||||
{%- if chill_person.fields.nationality == 'visible' or chill_person.fields.spoken_languages == 'visible'-%}
|
||||
<div class="grid-6">
|
||||
<figure class="person-details">
|
||||
<h2 class="chill-orange">{{ 'Administrative information'|trans|upper }}</h2>
|
||||
|
||||
{%- if chill_person.fields.nationality == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Nationality'|trans }} :</dt>
|
||||
<dd>
|
||||
@ -105,6 +128,8 @@ This view should receive those arguments:
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
{%- endif -%}
|
||||
{%- if chill_person.fields.spoken_languages == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{'Spoken languages'|trans}} :</dt>
|
||||
<dd>
|
||||
@ -117,6 +142,8 @@ This view should receive those arguments:
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
{%- endif -%}
|
||||
{%- if chill_person.fields.marital_status == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{'Marital status'|trans}} :</dt>
|
||||
<dd>
|
||||
@ -127,24 +154,53 @@ This view should receive those arguments:
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
{%- endif -%}
|
||||
|
||||
{% if is_granted('CHILL_PERSON_UPDATE', person) %}
|
||||
{{ include(edit_tmp_name, edit_tmp_args) }}
|
||||
{% endif %}
|
||||
</figure>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- if chill_person.fields.email == 'visible' or chill_person.fields.phonenumber == 'visible' -%}
|
||||
<div class="grid-6">
|
||||
<figure class="person-details">
|
||||
<h2 class="chill-blue"><i class="fa fa-envelope-o"></i> {{ 'Contact information'|trans|upper }}</h2>
|
||||
|
||||
{%- if chill_person.fields.address == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Address'|trans }}</dt>
|
||||
<dd>
|
||||
{%- if person.lastAddress is not empty -%}
|
||||
{{ address._render(person.lastAddress) }}
|
||||
<a href="{{ path('chill_person_address_edit', { 'person_id': person.id, 'address_id' : person.lastAddress.id } ) }}">
|
||||
{{ 'Edit'|trans }}
|
||||
</a><br/>
|
||||
<a href="{{ path('chill_person_address_list', { 'person_id': person.id } ) }}">
|
||||
{{ 'Addresses\'history'|trans }}
|
||||
</a>
|
||||
{%- else -%}
|
||||
<span class="chill-no-data-statement">{{ 'No address given'|trans }}</span>
|
||||
<a href="{{ path('chill_person_address_new', { 'person_id' : person.id } ) }}" class="">
|
||||
{{ 'Add an address'|trans }}
|
||||
</a>
|
||||
{%- endif -%}
|
||||
</dd>
|
||||
</dl>
|
||||
{%- endif -%}
|
||||
|
||||
{%- if chill_person.fields.email == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Email'|trans }} :</dt>
|
||||
<dd><pre>{{ person.email}} </pre></dd>
|
||||
</dl>
|
||||
{%- endif -%}
|
||||
{%- if chill_person.fields.phonenumber == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Phonenumber'|trans }} :</dt>
|
||||
<dd><pre>{{ person.phonenumber}} </pre></dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if is_granted('CHILL_PERSON_UPDATE', person) %}
|
||||
@ -152,6 +208,7 @@ This view should receive those arguments:
|
||||
{% endif %}
|
||||
</figure>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
|
||||
{% if cFGroup and (cFGroup.getActiveCustomFields|length > 0) %}
|
||||
|
@ -70,6 +70,7 @@
|
||||
{{ person.birthdate|localizeddate('long', 'none') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- if chill_person.fields.nationality == 'visible' -%}
|
||||
<div class="grid-3">
|
||||
<span class="open_sansbold">{{ 'Nationality'|trans|upper}} :</span>
|
||||
{% if person.nationality is not null %}
|
||||
@ -78,6 +79,8 @@
|
||||
{% trans %}Without nationality{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- if chill_person.fields.spoken_languages == 'visible' -%}
|
||||
<div class="grid-3">
|
||||
<span class="open_sansbold">{{ 'Spoken languages'|trans|upper}} :</span>
|
||||
{% if person.spokenLanguages|length == 0 %}
|
||||
@ -87,7 +90,8 @@
|
||||
{{ lang.name|localize_translatable_string }}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -104,4 +108,8 @@
|
||||
'args' : {'person_id': person.id },
|
||||
'activeRouteKey': activeRouteKey
|
||||
}) }}
|
||||
|
||||
<div class="block-post-menu">
|
||||
{{ chill_delegated_block('person_post_vertical_menu', { 'person': person } ) }}
|
||||
</div>
|
||||
{% endblock %}
|
189
Tests/Controller/PersonAddressControllerTest.php
Normal file
189
Tests/Controller/PersonAddressControllerTest.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 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\PersonBundle\Tests\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class PersonAddressControllerTest extends WebTestCase
|
||||
{
|
||||
/** @var \Doctrine\ORM\EntityManagerInterface The entity manager */
|
||||
protected $em;
|
||||
|
||||
/** @var Person The person on which the test is executed */
|
||||
protected static $person;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Chill\MainBundle\Entity\PostalCode
|
||||
*/
|
||||
protected $postalCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Symfony\Component\BrowserKit\Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
static::bootKernel();
|
||||
|
||||
$em = static::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager');
|
||||
|
||||
$center = $em->getRepository('ChillMainBundle:Center')
|
||||
->findOneBy(array('name' => 'Center A'));
|
||||
|
||||
self::$person = (new Person())
|
||||
->setLastName("Tested person")
|
||||
->setFirstName("Test")
|
||||
->setCenter($center)
|
||||
->setGender(Person::MALE_GENDER);
|
||||
|
||||
$em->persist(self::$person);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare client and create a random person
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
static::bootKernel();
|
||||
|
||||
$this->em = static::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager');
|
||||
|
||||
$this->postalCode = $this->em->getRepository('ChillMainBundle:PostalCode')
|
||||
->findOneBy(array('code' => 1000));
|
||||
|
||||
$this->client = static::createClient(array(), array(
|
||||
'PHP_AUTH_USER' => 'center a_social',
|
||||
'PHP_AUTH_PW' => 'password',
|
||||
));
|
||||
}
|
||||
|
||||
public static function tearDownAfter()
|
||||
{
|
||||
$this->refreshPerson();
|
||||
$this->em->remove(self::$person);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the person from the db
|
||||
*/
|
||||
protected function refreshPerson()
|
||||
{
|
||||
self::$person = $this->em->getRepository('ChillPersonBundle:Person')
|
||||
->find(self::$person->getId());
|
||||
}
|
||||
|
||||
public function testEmptyList()
|
||||
{
|
||||
$crawler = $this->client->request('GET', '/fr/person/'.
|
||||
self::$person->getId().'/address/list');
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isSuccessful());
|
||||
|
||||
$this->assertEquals(1, $crawler->filter('td:contains("Pas d\'adresse renseignée")')
|
||||
->count(),
|
||||
"assert that a message say 'no address given'");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testEmptyList
|
||||
*/
|
||||
public function testCreateAddress()
|
||||
{
|
||||
$crawler = $this->client->request('GET', '/fr/person/'.
|
||||
self::$person->getId().'/address/new');
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isSuccessful());
|
||||
|
||||
$form = $crawler->selectButton('Envoi')->form(array(
|
||||
'address[streetAddress1]' => 'Rue de la Paix, 50',
|
||||
'address[streetAddress2]' => $this->postalCode->getId(),
|
||||
'address[validFrom]' => '15-01-2016'
|
||||
));
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$crawler = $this->client->followRedirect();
|
||||
|
||||
$this->assertRegexp('|/fr/person/[0-9]{1,}/address/list|',
|
||||
$this->client->getHistory()->current()->getUri(),
|
||||
"assert that the current page is on |/fr/person/[0-9]{1,}/address/list|");
|
||||
$this->assertEquals(1, $crawler
|
||||
->filter('div.flash_message.success')
|
||||
->count(),
|
||||
"Asserting that the response page contains a success flash message");
|
||||
$this->assertEquals(1, $crawler
|
||||
->filter('td:contains("Rue de la Paix, 50")')
|
||||
->count(),
|
||||
"Asserting that the page contains the new address");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateAddress
|
||||
*/
|
||||
public function testUpdateAddress()
|
||||
{
|
||||
$this->refreshPerson();
|
||||
$address = self::$person->getLastAddress();
|
||||
|
||||
$crawler = $this->client->request('GET', '/fr/person/'.self::$person->getId()
|
||||
.'/address/'.$address->getId().'/edit');
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isSuccessful());
|
||||
|
||||
$form = $crawler->selectButton('Envoi')->form(array(
|
||||
'address[streetAddress1]' => 'Rue du Trou Normand, 15',
|
||||
'address[validFrom]' => '15-01-2015'
|
||||
));
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$crawler = $this->client->followRedirect();
|
||||
|
||||
$this->assertRegexp('|/fr/person/[0-9]{1,}/address/list|',
|
||||
$this->client->getHistory()->current()->getUri(),
|
||||
"assert that the current page is on |/fr/person/[0-9]{1,}/address/list|");
|
||||
$this->assertGreaterThan(0, $crawler
|
||||
->filter('div.flash_message.success')
|
||||
->count(),
|
||||
"Asserting that the response page contains a success flash message");
|
||||
$this->assertEquals(1, $crawler
|
||||
->filter('td:contains("Rue du Trou Normand")')
|
||||
->count(),
|
||||
"Asserting that the page contains the new address");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -240,6 +240,31 @@ class PersonControllerCreateTest extends WebTestCase
|
||||
|
||||
}
|
||||
|
||||
public function testReviewExistingDetectionInversedLastNameWithFirstName()
|
||||
{
|
||||
$client = $this->getAuthenticatedClient();
|
||||
|
||||
$crawler = $client->request('GET', '/fr/person/new');
|
||||
|
||||
//test the page is loaded before continuing
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$form = $crawler->selectButton("Ajouter la personne")->form();
|
||||
$form = $this->fillAValidCreationForm($form, 'Charline', 'dd');
|
||||
$client->submit($form);
|
||||
|
||||
$this->assertContains('Depardieu', $client->getCrawler()->text(),
|
||||
"check that the page has detected the lastname of a person existing in database");
|
||||
|
||||
//inversion
|
||||
$form = $crawler->selectButton("Ajouter la personne")->form();
|
||||
$form = $this->fillAValidCreationForm($form, 'dd', 'Charline');
|
||||
$client->submit($form);
|
||||
|
||||
$this->assertContains('Depardieu', $client->getCrawler()->text(),
|
||||
"check that the page has detected the lastname of a person existing in database");
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
static::bootKernel();
|
||||
|
@ -96,6 +96,24 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
"The person edit form is accessible");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the configurable fields are present
|
||||
*
|
||||
* @group configurable_fields
|
||||
*/
|
||||
public function testHiddenFielsArePresent()
|
||||
{
|
||||
$crawler = $this->client->request('GET', $this->editUrl);
|
||||
|
||||
$configurables = array('placeOfBirth', 'phonenumber', 'email',
|
||||
'countryOfBirth', 'nationality', 'spokenLanguages', 'maritalStatus');
|
||||
$form = $crawler->selectButton('Submit')->form(); //;
|
||||
|
||||
foreach($configurables as $key) {
|
||||
$this->assertTrue($form->has('chill_personbundle_person['.$key.']'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the edit page of a given person is not accessible for a user
|
||||
* of another center of the person
|
||||
|
211
Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php
Normal file
211
Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php
Normal file
@ -0,0 +1,211 @@
|
||||
<?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\PersonBundle\Tests\Controller;
|
||||
|
||||
//ini_set('memory_limit', '-1');
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* Test the edition of persons
|
||||
*
|
||||
* As I am logged in as "center a_social"
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class PersonControllerUpdateWithHiddenFieldsTest extends WebTestCase
|
||||
{
|
||||
/** @var \Doctrine\ORM\EntityManagerInterface The entity manager */
|
||||
private $em;
|
||||
|
||||
/** @var Person The person on which the test is executed */
|
||||
private $person;
|
||||
|
||||
/** @var string The url using for editing the person's information */
|
||||
private $editUrl;
|
||||
|
||||
/** @var string The url using for seeing the person's information */
|
||||
private $viewUrl;
|
||||
|
||||
/**
|
||||
* Prepare client and create a random person
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
static::bootKernel(array('environment' => 'test_with_hidden_fields'));
|
||||
|
||||
$this->em = static::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager');
|
||||
|
||||
$center = $this->em->getRepository('ChillMainBundle:Center')
|
||||
->findOneBy(array('name' => 'Center A'));
|
||||
|
||||
$this->person = (new Person())
|
||||
->setLastName("My Beloved")
|
||||
->setFirstName("Jesus")
|
||||
->setCenter($center)
|
||||
->setGender(Person::MALE_GENDER);
|
||||
|
||||
$this->em->persist($this->person);
|
||||
$this->em->flush();
|
||||
|
||||
$this->editUrl = '/en/person/'.$this->person->getId().'/general/edit';
|
||||
$this->viewUrl = '/en/person/'.$this->person->getId().'/general';
|
||||
|
||||
$this->client = static::createClient(
|
||||
array(
|
||||
'environment' => 'test_with_hidden_fields'
|
||||
),
|
||||
array(
|
||||
'PHP_AUTH_USER' => 'center a_social',
|
||||
'PHP_AUTH_PW' => 'password',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the person from the db
|
||||
*/
|
||||
protected function refreshPerson()
|
||||
{
|
||||
$this->person = $this->em->getRepository('ChillPersonBundle:Person')
|
||||
->find($this->person->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the edit page are accessible
|
||||
*/
|
||||
public function testEditPageIsSuccessful()
|
||||
{
|
||||
$this->client->request('GET', $this->editUrl);
|
||||
$this->assertTrue($this->client->getResponse()->isSuccessful(),
|
||||
"The person edit form is accessible");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the configurable fields are absent
|
||||
*
|
||||
* @group configurable_fields
|
||||
*/
|
||||
public function testHiddenFielsAreAbsent()
|
||||
{
|
||||
$crawler = $this->client->request('GET', $this->editUrl);
|
||||
|
||||
$configurables = array('placeOfBirth', 'phonenumber', 'email',
|
||||
'countryOfBirth', 'nationality', 'spokenLanguages', 'maritalStatus');
|
||||
$form = $crawler->selectButton('Submit')->form(); //;
|
||||
|
||||
foreach($configurables as $key) {
|
||||
$this->assertFalse($form->has('chill_personbundle_person['.$key.']'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the edition of a field
|
||||
*
|
||||
* Given I fill the field with $value
|
||||
* And I submit the form
|
||||
* Then I am redirected to the 'general' page
|
||||
* And the person is updated in the db
|
||||
*
|
||||
* @dataProvider validTextFieldsProvider
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
* @param \Closure $callback
|
||||
*/
|
||||
public function testEditTextField($field, $value, \Closure $callback)
|
||||
{
|
||||
$crawler = $this->client->request('GET', $this->editUrl);
|
||||
|
||||
$form = $crawler->selectButton('Submit')
|
||||
->form();
|
||||
//transform countries into value if needed
|
||||
switch ($field) {
|
||||
case 'nationality':
|
||||
case 'countryOfBirth':
|
||||
if ($value !== NULL) {
|
||||
$country = $this->em->getRepository('ChillMainBundle:Country')
|
||||
->findOneByCountryCode($value);
|
||||
$transformedValue = $country->getId();
|
||||
} else {
|
||||
$transformedValue = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$transformedValue = $value;
|
||||
}
|
||||
|
||||
$form->get('chill_personbundle_person['.$field. ']')
|
||||
->setValue($transformedValue);
|
||||
|
||||
$this->client->submit($form);
|
||||
$this->refreshPerson();
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isRedirect($this->viewUrl),
|
||||
'the page is redirected to general view');
|
||||
$this->assertEquals($value, $callback($this->person),
|
||||
'the value '.$field.' is updated in db');
|
||||
|
||||
$crawler = $this->client->followRedirect();
|
||||
$this->assertGreaterThan(0, $crawler->filter('.success')->count(),
|
||||
'a element .success is shown');
|
||||
|
||||
if($field == 'birthdate' or $field == 'memo' or $field == 'countryOfBirth' or $field == 'nationality'
|
||||
or $field == 'gender') {
|
||||
// we do not perform test on the web page contents.
|
||||
} else {
|
||||
$this->assertGreaterThan(0, $crawler->filter('html:contains("'.$value.'")')->count());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* provide valid values to test, with field name and
|
||||
* a function to find the value back from person entity
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function validTextFieldsProvider()
|
||||
{
|
||||
return array(
|
||||
['firstName', 'random Value', function(Person $person) { return $person->getFirstName(); } ],
|
||||
['lastName' , 'random Value', function(Person $person) { return $person->getLastName(); } ],
|
||||
['birthdate', '15-12-1980', function(Person $person) { return $person->getBirthdate()->format('d-m-Y'); }],
|
||||
['memo', 'jfkdlmq jkfldmsq jkmfdsq', function(Person $person) { return $person->getMemo(); }],
|
||||
['birthdate', '', function(Person $person) { return $person->getBirthdate(); }],
|
||||
['gender', Person::FEMALE_GENDER, function(Person $person) { return $person->getGender(); }],
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
$this->refreshPerson();
|
||||
$this->em->remove($this->person);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
private function getVeryLongText()
|
||||
{
|
||||
return <<<EOT
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse molestie at enim id auctor. Vivamus malesuada elit ipsum, ac mollis ex facilisis sit amet. Phasellus accumsan, quam ut aliquet accumsan, augue ligula consequat erat, condimentum iaculis orci magna egestas eros. In vel blandit sapien. Duis ut dui vitae tortor iaculis malesuada vitae vitae lorem. Morbi efficitur dolor orci, a rhoncus urna blandit quis. Aenean at placerat dui, ut tincidunt nulla. In ultricies tempus ligula ac rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce urna nibh, placerat vel auctor sed, maximus quis magna. Vivamus quam ante, consectetur vel feugiat quis, aliquet id ante. Integer gravida erat dignissim ante commodo mollis. Donec imperdiet mauris elit, nec blandit dolor feugiat ut. Proin iaculis enim ut tortor pretium commodo. Etiam aliquet hendrerit dolor sed fringilla. Vestibulum facilisis nibh tincidunt dui egestas, vitae congue mi imperdiet. Duis vulputate ultricies lectus id cursus. Fusce bibendum sem dignissim, bibendum purus quis, mollis ex. Cras ac est justo. Duis congue mattis ipsum, vitae sagittis justo dictum sit amet. Duis aliquam pharetra sem, non laoreet ante laoreet ac. Mauris ornare mi tempus rutrum consequat.
|
||||
EOT;
|
||||
}
|
||||
}
|
@ -61,6 +61,8 @@ class PersonControllerViewTest extends WebTestCase
|
||||
|
||||
/**
|
||||
* Test if the view page is accessible
|
||||
*
|
||||
* @group configurable_fields
|
||||
*/
|
||||
public function testViewPerson()
|
||||
{
|
||||
@ -76,6 +78,10 @@ class PersonControllerViewTest extends WebTestCase
|
||||
|
||||
$this->assertGreaterThan(0, $crawler->filter('html:contains("Tested Person")')->count());
|
||||
$this->assertGreaterThan(0, $crawler->filter('html:contains("Réginald")')->count());
|
||||
$this->assertContains('Email addresses', $crawler->text());
|
||||
$this->assertContains('Phonenumber', $crawler->text());
|
||||
$this->assertContains('Langues parlées', $crawler->text());
|
||||
$this->assertContains(/* Etat */ 'civil', $crawler->text());
|
||||
}
|
||||
|
||||
/**
|
||||
|
107
Tests/Controller/PersonControllerViewWithHiddenFieldsTest.php
Normal file
107
Tests/Controller/PersonControllerViewWithHiddenFieldsTest.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?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\PersonBundle\Tests\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
/**
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* @author Marc Ducobu <marc.ducobu@champs-libres.coop>
|
||||
*/
|
||||
class PersonControllerViewTestWithHiddenFields extends WebTestCase
|
||||
{
|
||||
/** @var \Doctrine\ORM\EntityManagerInterface The entity manager */
|
||||
private $em;
|
||||
|
||||
/** @var Person A person used on which to run the test */
|
||||
private $person;
|
||||
|
||||
/** @var String The url to view the person details */
|
||||
private $viewUrl;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
static::bootKernel(array('environment' => 'test_with_hidden_fields'));
|
||||
|
||||
$this->em = static::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager');
|
||||
|
||||
$center = $this->em->getRepository('ChillMainBundle:Center')
|
||||
->findOneBy(array('name' => 'Center A'));
|
||||
|
||||
$this->person = (new Person())
|
||||
->setLastName("Tested Person")
|
||||
->setFirstName("Réginald")
|
||||
->setCenter($center)
|
||||
->setGender(Person::MALE_GENDER);
|
||||
|
||||
$this->em->persist($this->person);
|
||||
$this->em->flush();
|
||||
|
||||
$this->viewUrl = '/en/person/'.$this->person->getId().'/general';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the view page is accessible
|
||||
*
|
||||
* @group configurable_fields
|
||||
*/
|
||||
public function testViewPerson()
|
||||
{
|
||||
$client = static::createClient(
|
||||
array('environment' => 'test_with_hidden_fields'),
|
||||
array(
|
||||
'PHP_AUTH_USER' => 'center a_social',
|
||||
'PHP_AUTH_PW' => 'password',
|
||||
'HTTP_ACCEPT_LANGUAGE' => 'fr'
|
||||
)
|
||||
);
|
||||
|
||||
$crawler = $client->request('GET', $this->viewUrl);
|
||||
$response = $client->getResponse();
|
||||
|
||||
$this->assertTrue($response->isSuccessful());
|
||||
|
||||
$this->assertGreaterThan(0, $crawler->filter('html:contains("Tested Person")')->count());
|
||||
$this->assertGreaterThan(0, $crawler->filter('html:contains("Réginald")')->count());
|
||||
$this->assertNotContains('Email addresses', $crawler->text());
|
||||
$this->assertNotContains('Phonenumber', $crawler->text());
|
||||
$this->assertNotContains('Langues parlées', $crawler->text());
|
||||
$this->assertNotContains(/* Etat */ 'civil', $crawler->text());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the person from the db
|
||||
*/
|
||||
protected function refreshPerson()
|
||||
{
|
||||
$this->person = $this->em->getRepository('ChillPersonBundle:Person')
|
||||
->find($this->person->getId());
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
$this->refreshPerson();
|
||||
$this->em->remove($this->person);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
# config/config_test.yml
|
||||
imports:
|
||||
- { resource: config.yml } #here we import a config.yml file, this is not required
|
||||
|
||||
framework:
|
||||
test: ~
|
||||
session:
|
||||
storage_id: session.storage.filesystem
|
||||
|
||||
chill_person:
|
||||
person_fields:
|
||||
nationality: hidden
|
||||
email: hidden
|
||||
place_of_birth: hidden
|
||||
phonenumber: hidden
|
||||
country_of_birth: hidden
|
||||
marital_status: hidden
|
||||
spoken_languages: hidden
|
181
Tests/Form/Type/PickPersonTypeTest.php
Normal file
181
Tests/Form/Type/PickPersonTypeTest.php
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 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\PersonBundle\Tests\Form\Type;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Chill\PersonBundle\Form\Type\PickPersonType;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class PickPersonTypeTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var \Chill\MainBundle\Entity\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Symfony\Component\Form\FormFactoryInterface
|
||||
*/
|
||||
protected $formFactory;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->container = self::$kernel->getContainer();
|
||||
|
||||
$this->user = $this->container->get('doctrine.orm.entity_manager')
|
||||
->getRepository('ChillMainBundle:User')
|
||||
->findOneBy(array('username' => 'multi_center'));
|
||||
|
||||
$this->formFactory = $this->container->get('form.factory');
|
||||
|
||||
$token = (new UsernamePasswordToken($this->user, 'password', 'firewall'));
|
||||
$this->container->get('security.token_storage')
|
||||
->setToken($token);
|
||||
}
|
||||
|
||||
public function testWithoutOption()
|
||||
{
|
||||
$form = $this->formFactory
|
||||
->createBuilder(PickPersonType::class, null, array())
|
||||
->getForm();
|
||||
|
||||
$this->assertInstanceOf(\Symfony\Component\Form\FormInterface::class,
|
||||
$form);
|
||||
|
||||
// transform into a view to have data-center attr
|
||||
$view = $form->createView();
|
||||
|
||||
$centerIds = array();
|
||||
|
||||
/* @var $centerIds \Symfony\Component\Form\ChoiceList\View\ChoiceView */
|
||||
foreach($view->vars['choices'] as $choice) {
|
||||
$centerIds[] = $choice->attr['data-center'];
|
||||
}
|
||||
|
||||
$this->assertEquals(2, count(array_unique($centerIds)),
|
||||
"test that the form contains people from 2 centers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the form with an option 'centers' with an unique center
|
||||
* entity (not in an array)
|
||||
*/
|
||||
public function testWithOptionCenter()
|
||||
{
|
||||
$center = $this->container->get('doctrine.orm.entity_manager')
|
||||
->getRepository('ChillMainBundle:Center')
|
||||
->findOneBy(array('name' => 'Center A'))
|
||||
;
|
||||
|
||||
$form = $this->formFactory
|
||||
->createBuilder(PickPersonType::class, null, array(
|
||||
'centers' => $center
|
||||
))
|
||||
->getForm();
|
||||
|
||||
// transform into a view to have data-center attr
|
||||
$view = $form->createView();
|
||||
|
||||
/* @var $centerIds \Symfony\Component\Form\ChoiceList\View\ChoiceView */
|
||||
foreach($view->vars['choices'] as $choice) {
|
||||
$centerIds[] = $choice->attr['data-center'];
|
||||
}
|
||||
|
||||
$this->assertEquals(1, count(array_unique($centerIds)),
|
||||
"test that the form contains people from only one centers");
|
||||
|
||||
$this->assertEquals($center->getId(), array_unique($centerIds)[0]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the form with multiple centers
|
||||
*/
|
||||
public function testWithOptionCenters()
|
||||
{
|
||||
$centers = $this->container->get('doctrine.orm.entity_manager')
|
||||
->getRepository('ChillMainBundle:Center')
|
||||
->findAll()
|
||||
;
|
||||
|
||||
$form = $this->formFactory
|
||||
->createBuilder(PickPersonType::class, null, array(
|
||||
'centers' => $centers
|
||||
))
|
||||
->getForm();
|
||||
|
||||
// transform into a view to have data-center attr
|
||||
$view = $form->createView();
|
||||
|
||||
/* @var $centerIds \Symfony\Component\Form\ChoiceList\View\ChoiceView */
|
||||
foreach($view->vars['choices'] as $choice) {
|
||||
$centerIds[] = $choice->attr['data-center'];
|
||||
}
|
||||
|
||||
$this->assertEquals(2, count(array_unique($centerIds)),
|
||||
"test that the form contains people from only one centers");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* test with an invalid center type in the option 'centers' (in an array)
|
||||
*
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testWithInvalidOptionCenters()
|
||||
{
|
||||
|
||||
$form = $this->formFactory
|
||||
->createBuilder(PickPersonType::class, null, array(
|
||||
'centers' => array('string')
|
||||
))
|
||||
->getForm();
|
||||
}
|
||||
|
||||
public function testWithOptionRoleInvalid()
|
||||
{
|
||||
$form = $this->formFactory
|
||||
->createBuilder(PickPersonType::class, null, array(
|
||||
'role' => new \Symfony\Component\Security\Core\Role\Role('INVALID')
|
||||
))
|
||||
->getForm();
|
||||
|
||||
// transform into a view to have data-center attr
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertEquals(0, count($view->vars['choices']));
|
||||
}
|
||||
|
||||
}
|
@ -16,13 +16,12 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "~5.5",
|
||||
"php": "~5.6|~7",
|
||||
"twig/extensions": "~1.0",
|
||||
"symfony/assetic-bundle": "~2.3",
|
||||
"symfony/monolog-bundle": "^2.7",
|
||||
"symfony/framework-bundle": "~2.7",
|
||||
"symfony/yaml": "~2.7",
|
||||
"symfony/symfony": "~2.7",
|
||||
"symfony/symfony": "~2.8",
|
||||
"doctrine/dbal": "~2.5",
|
||||
"doctrine/orm": "~2.4",
|
||||
"doctrine/common": "~2.4",
|
||||
@ -31,12 +30,10 @@
|
||||
"chill-project/custom-fields": "dev-master@dev",
|
||||
"doctrine/doctrine-fixtures-bundle": "~2.2",
|
||||
"champs-libres/composer-bundle-migration": "~1.0",
|
||||
"doctrine/doctrine-migrations-bundle": "dev-master@dev",
|
||||
"doctrine/doctrine-migrations-bundle": "~1.1",
|
||||
"doctrine/migrations": "~1.0@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/dom-crawler": "2.5",
|
||||
"symfony/security": "~2.5",
|
||||
"symfony/phpunit-bridge": "^2.7",
|
||||
"fzaninotto/faker": "~1"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user