mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge branch 'master' into household_filiation
This commit is contained in:
@@ -504,6 +504,8 @@ class ApiController extends AbstractCRUDController
|
||||
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity, [$postedData])
|
||||
);
|
||||
}
|
||||
|
||||
throw new \Exception('Unable to handle such request method.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -342,13 +342,19 @@ class CRUDController extends AbstractController
|
||||
*/
|
||||
protected function buildQueryEntities(string $action, Request $request)
|
||||
{
|
||||
return $this->getDoctrine()->getManager()
|
||||
$query = $this->getDoctrine()->getManager()
|
||||
->createQueryBuilder()
|
||||
->select('e')
|
||||
->from($this->getEntityClass(), 'e')
|
||||
;
|
||||
|
||||
$this->customizeQuery($action, $request, $query);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function customizeQuery(string $action, Request $request, $query): void {}
|
||||
|
||||
/**
|
||||
* Query the entity.
|
||||
*
|
||||
|
@@ -8,6 +8,7 @@ use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverInterface;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
|
||||
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass;
|
||||
@@ -38,6 +39,8 @@ class ChillMainBundle extends Bundle
|
||||
->addTag('chill_main.center_resolver');
|
||||
$container->registerForAutoconfiguration(ScopeResolverInterface::class)
|
||||
->addTag('chill_main.scope_resolver');
|
||||
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
|
||||
->addTag('chill.render_entity');
|
||||
|
||||
$container->addCompilerPass(new SearchableServicesCompilerPass());
|
||||
$container->addCompilerPass(new ConfigConsistencyCompilerPass());
|
||||
|
@@ -10,18 +10,18 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
/**
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop
|
||||
*
|
||||
*
|
||||
*/
|
||||
class LoadCountriesCommand extends Command
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
|
||||
private $availableLanguages;
|
||||
|
||||
|
||||
/**
|
||||
* LoadCountriesCommand constructor.
|
||||
*
|
||||
@@ -34,7 +34,7 @@ class LoadCountriesCommand extends Command
|
||||
$this->availableLanguages=$availableLanguages;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-PHPdoc)
|
||||
* @see \Symfony\Component\Console\Command\Command::configure()
|
||||
@@ -45,7 +45,7 @@ class LoadCountriesCommand extends Command
|
||||
->setDescription('Load or update countries in db. This command does not delete existing countries, '.
|
||||
'but will update names according to available languages');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-PHPdoc)
|
||||
* @see \Symfony\Component\Console\Command\Command::execute()
|
||||
@@ -54,43 +54,44 @@ class LoadCountriesCommand extends Command
|
||||
{
|
||||
$countries = static::prepareCountryList($this->availableLanguages);
|
||||
$em = $this->entityManager;
|
||||
|
||||
|
||||
foreach($countries as $country) {
|
||||
$countryStored = $em->getRepository('ChillMainBundle:Country')
|
||||
->findOneBy(array('countryCode' => $country->getCountryCode()));
|
||||
|
||||
|
||||
if (NULL === $countryStored) {
|
||||
$em->persist($country);
|
||||
} else {
|
||||
$countryStored->setName($country->getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
public static function prepareCountryList($languages)
|
||||
{
|
||||
$regionBundle = Intl::getRegionBundle();
|
||||
|
||||
$countries = [];
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$countries[$language] = $regionBundle->getCountryNames($language);
|
||||
}
|
||||
|
||||
|
||||
$countryEntities = array();
|
||||
|
||||
|
||||
foreach ($countries[$languages[0]] as $countryCode => $name) {
|
||||
$names = array();
|
||||
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$names[$language] = $countries[$language][$countryCode];
|
||||
}
|
||||
|
||||
|
||||
$country = new \Chill\MainBundle\Entity\Country();
|
||||
$country->setName($names)->setCountryCode($countryCode);
|
||||
$countryEntities[] = $country;
|
||||
}
|
||||
|
||||
|
||||
return $countryEntities;
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
@@ -20,8 +21,17 @@ class AddressReferenceAPIController extends ApiController
|
||||
|
||||
$qb->where('e.postcode = :postal_code')
|
||||
->setParameter('postal_code', $request->query->get('postal_code'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
|
||||
{
|
||||
$query->addOrderBy('e.street', 'ASC');
|
||||
$query->addOrderBy('e.streetNumber', 'ASC');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -41,4 +41,9 @@ class AdminController extends AbstractController
|
||||
return $this->render('@ChillMain/Admin/layout_permissions.html.twig');
|
||||
}
|
||||
|
||||
public function indexLocationsAction()
|
||||
{
|
||||
return $this->render('@ChillMain/Admin/layout_location.html.twig');
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -20,11 +20,17 @@ class LocationApiController extends ApiController
|
||||
$query->expr()->eq('e.createdBy', ':user'),
|
||||
$query->expr()->gte('e.createdAt', ':dateBefore')
|
||||
),
|
||||
$query->expr()->eq('e.availableForUsers', "'TRUE'")
|
||||
$query->expr()->andX(
|
||||
$query->expr()->eq('e.availableForUsers', "'TRUE'"),
|
||||
$query->expr()->eq('e.active', "'TRUE'"),
|
||||
$query->expr()->isNotNull('e.name'),
|
||||
$query->expr()->neq('e.name', ':emptyString'),
|
||||
)
|
||||
))
|
||||
->setParameters([
|
||||
'user' => $this->getUser(),
|
||||
'dateBefore' => (new \DateTime())->sub(new \DateInterval('P6M'))
|
||||
'dateBefore' => (new \DateTime())->sub(new \DateInterval('P6M')),
|
||||
'emptyString' => '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
23
src/Bundle/ChillMainBundle/Controller/LocationController.php
Normal file
23
src/Bundle/ChillMainBundle/Controller/LocationController.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class LocationController extends CRUDController
|
||||
{
|
||||
protected function customizeQuery(string $action, Request $request, $query): void
|
||||
{
|
||||
$query->where('e.availableForUsers = true'); //TODO not working
|
||||
}
|
||||
|
||||
protected function createEntity(string $action, Request $request): object
|
||||
{
|
||||
$entity = parent::createEntity($action, $request);
|
||||
|
||||
$entity->setAvailableForUsers(true);
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class LocationTypeApiController
|
||||
*
|
||||
* @package Chill\MainBundle\Controller
|
||||
* @author Champs Libres
|
||||
*/
|
||||
class LocationTypeApiController extends ApiController
|
||||
{
|
||||
public function customizeQuery(string $action, Request $request, $query): void
|
||||
{
|
||||
$query->andWhere(
|
||||
$query->expr()->andX(
|
||||
$query->expr()->eq('e.availableForUsers', "'TRUE'"),
|
||||
$query->expr()->eq('e.active', "'TRUE'"),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
|
||||
|
||||
class LocationTypeController extends CRUDController
|
||||
{
|
||||
}
|
@@ -24,26 +24,26 @@ class LoadLanguages extends AbstractFixture implements ContainerAwareInterface,
|
||||
// Array of ancien languages (to exclude)
|
||||
private $ancientToExclude = ["ang", "egy", "fro", "goh", "grc", "la", "non", "peo", "pro", "sga",
|
||||
"dum", "enm", "frm", "gmh", "mga", "akk", "phn", "zxx", "got", "und"];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
|
||||
public function setContainer(ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
|
||||
public function getOrder() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
public function load(ObjectManager $manager) {
|
||||
|
||||
|
||||
echo "loading languages... \n";
|
||||
|
||||
|
||||
foreach (Intl::getLanguageBundle()->getLanguageNames() as $code => $language) {
|
||||
if (
|
||||
!in_array($code, $this->regionalVersionToInclude)
|
||||
@@ -58,23 +58,24 @@ class LoadLanguages extends AbstractFixture implements ContainerAwareInterface,
|
||||
$manager->persist($lang);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* prepare names for languages
|
||||
*
|
||||
* @param string $languageCode
|
||||
* Prepare names for languages.
|
||||
*
|
||||
* @return string[] languages name indexed by available language code
|
||||
*/
|
||||
private function prepareName($languageCode) {
|
||||
private function prepareName(string $languageCode): array {
|
||||
$names = [];
|
||||
|
||||
foreach ($this->container->getParameter('chill_main.available_languages') as $lang) {
|
||||
$names[$lang] = Intl::getLanguageBundle()->getLanguageName($languageCode);
|
||||
}
|
||||
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\DataFixtures\ORM;
|
||||
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\Entity\LocationType;
|
||||
|
||||
/**
|
||||
* Load location types into database
|
||||
*
|
||||
* @author Champs Libres
|
||||
*/
|
||||
class LoadLocationType extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
public function setContainer(ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function getOrder() {
|
||||
return 52;
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager): void {
|
||||
|
||||
echo "loading some location type... \n";
|
||||
|
||||
$arr = [
|
||||
[
|
||||
'name' => ['fr' => 'Mairie'],
|
||||
'address_required' => LocationType::STATUS_OPTIONAL
|
||||
],
|
||||
[
|
||||
'name' => ['fr' => 'Guichet d\'accueil'],
|
||||
'address_required' => LocationType::STATUS_OPTIONAL
|
||||
],
|
||||
[
|
||||
'name' => ['fr' => 'Domicile de l\'usager'],
|
||||
'address_required' => LocationType::STATUS_REQUIRED
|
||||
],
|
||||
[
|
||||
'name' => ['fr' => 'Centre d\'aide sociale'],
|
||||
'address_required' => LocationType::STATUS_OPTIONAL
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($arr as $a) {
|
||||
$locationType = (new LocationType())
|
||||
->setTitle($a['name'])
|
||||
->setAvailableForUsers(true)
|
||||
->setActive(true)
|
||||
->setAddressRequired($a['address_required']);
|
||||
$manager->persist($locationType);
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
}
|
@@ -135,7 +135,6 @@ EOF;
|
||||
85110,ST VINCENT STERLANGES,FR,85276,46.7397220689,-1.08371759277,INSEE
|
||||
85110,SIGOURNAIS,FR,85282,46.7140097406,-0.98747730882,INSEE
|
||||
85110,CHANTONNAY,FR,85051,46.6690167793,-1.04372588019,INSEE
|
||||
85110,CHANTONNAY,FR,85051,46.6690167793,-1.04372588019,INSEE
|
||||
85110,ST PROUANT,FR,85266,46.7502017421,-0.974504061491,INSEE
|
||||
85120,LA CHAPELLE AUX LYS,FR,85053,46.6221916887,-0.642706103195,INSEE
|
||||
85120,VOUVANT,FR,85305,46.5626835135,-0.764380170382,INSEE
|
||||
@@ -156,11 +155,8 @@ EOF;
|
||||
85130,ST AUBIN DES ORMEAUX,FR,85198,46.9958175597,-1.04216568722,INSEE
|
||||
85140,ESSARTS EN BOCAGE,FR,85084,46.7806739038,-1.22925967851,INSEE
|
||||
85140,LA MERLATIERE,FR,85142,46.7557703112,-1.29794577,INSEE
|
||||
85140,ESSARTS EN BOCAGE,FR,85084,46.7806739038,-1.22925967851,INSEE
|
||||
85140,ESSARTS EN BOCAGE,FR,85084,46.7806739038,-1.22925967851,INSEE
|
||||
85140,ST MARTIN DES NOYERS,FR,85246,46.7239461187,-1.20379080965,INSEE
|
||||
85140,CHAUCHE,FR,85064,46.8282791899,-1.27090860656,INSEE
|
||||
85140,ESSARTS EN BOCAGE,FR,85084,46.7806739038,-1.22925967851,INSEE
|
||||
85150,ST MATHURIN,FR,85250,46.5686332748,-1.70787622288,INSEE
|
||||
85150,MARTINET,FR,85138,46.6620680463,-1.6772013304,INSEE
|
||||
85150,STE FLAIVE DES LOUPS,FR,85211,46.611019489,-1.58031627863,INSEE
|
||||
@@ -170,14 +166,12 @@ EOF;
|
||||
85150,LE GIROUARD,FR,85099,46.5726064909,-1.58872487716,INSEE
|
||||
85150,LANDERONDE,FR,85118,46.6549237031,-1.57351777893,INSEE
|
||||
85150,LES ACHARDS,FR,85152,46.6163645636,-1.65038156849,INSEE
|
||||
85150,LES ACHARDS,FR,85152,46.6163645636,-1.65038156849,INSEE
|
||||
85150,VAIRE,FR,85298,46.6055340621,-1.74863672042,INSEE
|
||||
85160,ST JEAN DE MONTS,FR,85234,46.8021968737,-2.04839789308,INSEE
|
||||
85170,BELLEVIGNY,FR,85019,46.7756383534,-1.43313700054,INSEE
|
||||
85170,LE POIRE SUR VIE,FR,85178,46.769919754,-1.50488626452,INSEE
|
||||
85170,BEAUFOU,FR,85015,46.8191122027,-1.52479250801,INSEE
|
||||
85170,DOMPIERRE SUR YON,FR,85081,46.7599858068,-1.37275519417,INSEE
|
||||
85170,BELLEVIGNY,FR,85019,46.7756383534,-1.43313700054,INSEE
|
||||
85170,LES LUCS SUR BOULOGNE,FR,85129,46.8527299002,-1.48398928084,INSEE
|
||||
85170,ST DENIS LA CHEVASSE,FR,85208,46.8325959261,-1.3830312677,INSEE
|
||||
85180,LES SABLES D OLONNE,FR,85194,46.5007612799,-1.79255128677,INSEE
|
||||
@@ -191,14 +185,11 @@ EOF;
|
||||
85200,FONTENAY LE COMTE,FR,85092,46.4563186117,-0.793449510859,INSEE
|
||||
85200,MERVENT,FR,85143,46.5325327351,-0.748519927998,INSEE
|
||||
85200,DOIX LES FONTAINES,FR,85080,46.3849492327,-0.806840287485,INSEE
|
||||
85200,FONTENAY LE COMTE,FR,85092,46.4563186117,-0.793449510859,INSEE
|
||||
85200,LONGEVES,FR,85126,46.4722105292,-0.858917690239,INSEE
|
||||
85200,ST MARTIN DE FRAIGNEAU,FR,85244,46.4289052087,-0.758948963227,INSEE
|
||||
85200,SERIGNE,FR,85281,46.5054321828,-0.848819460581,INSEE
|
||||
85200,BOURNEAU,FR,85033,46.5476882922,-0.813838020265,INSEE
|
||||
85200,ST MICHEL LE CLOUCQ,FR,85256,46.4861591475,-0.743056336646,INSEE
|
||||
85200,AUCHAY SUR VENDEE,FR,85009,46.4474386161,-0.876574265149,INSEE
|
||||
85200,DOIX LES FONTAINES,FR,85080,46.3849492327,-0.806840287485,INSEE
|
||||
85200,MONTREUIL,FR,85148,46.3973419593,-0.840846860992,INSEE
|
||||
85200,L ORBRIE,FR,85167,46.4997145725,-0.77427886573,INSEE
|
||||
85210,ST JEAN DE BEUGNE,FR,85233,46.5196817523,-1.10826075013,INSEE
|
||||
@@ -238,12 +229,9 @@ EOF;
|
||||
85260,LA COPECHAGNIERE,FR,85072,46.8523980181,-1.34349746898,INSEE
|
||||
85260,L HERBERGEMENT,FR,85108,46.9166207979,-1.37033557148,INSEE
|
||||
85260,MONTREVERD,FR,85197,46.9277672307,-1.4126154924,INSEE
|
||||
85260,MONTREVERD,FR,85197,46.9277672307,-1.4126154924,INSEE
|
||||
85260,MONTREVERD,FR,85197,46.9277672307,-1.4126154924,INSEE
|
||||
85260,LES BROUZILS,FR,85038,46.8854235235,-1.33186892233,INSEE
|
||||
85270,NOTRE DAME DE RIEZ,FR,85189,46.7532179022,-1.8935292542,INSEE
|
||||
85270,ST HILAIRE DE RIEZ,FR,85226,46.7432732188,-1.96439228965,INSEE
|
||||
85270,ST HILAIRE DE RIEZ,FR,85226,46.7432732188,-1.96439228965,INSEE
|
||||
85280,LA FERRIERE,FR,85089,46.7215872927,-1.33469332327,INSEE
|
||||
85290,MORTAGNE SUR SEVRE,FR,85151,46.9910941319,-0.946500033344,INSEE
|
||||
85290,ST LAURENT SUR SEVRE,FR,85238,46.9506837971,-0.901123752328,INSEE
|
||||
@@ -254,8 +242,6 @@ EOF;
|
||||
85300,SALLERTAINE,FR,85280,46.8659054157,-1.94894081389,INSEE
|
||||
85310,LA CHAIZE LE VICOMTE,FR,85046,46.6729533879,-1.29188591019,INSEE
|
||||
85310,NESMY,FR,85160,46.5921936479,-1.40947698594,INSEE
|
||||
85310,LA CHAIZE LE VICOMTE,FR,85046,46.6729533879,-1.29188591019,INSEE
|
||||
85310,RIVES DE L YON,FR,85213,46.605637391,-1.3354497172,INSEE
|
||||
85310,RIVES DE L YON,FR,85213,46.605637391,-1.3354497172,INSEE
|
||||
85310,LE TABLIER,FR,85285,46.5596307281,-1.32788759657,INSEE
|
||||
85320,CHATEAU GUIBERT,FR,85061,46.5741109302,-1.25524886228,INSEE
|
||||
@@ -263,11 +249,9 @@ EOF;
|
||||
85320,ROSNAY,FR,85193,46.5324344973,-1.3007139449,INSEE
|
||||
85320,BESSAY,FR,85023,46.5397253861,-1.17028433093,INSEE
|
||||
85320,LA BRETONNIERE LA CLAYE,FR,85036,46.4879459421,-1.26773426545,INSEE
|
||||
85320,LA BRETONNIERE LA CLAYE,FR,85036,46.4879459421,-1.26773426545,INSEE
|
||||
85320,CORPE,FR,85073,46.5050234767,-1.17034425311,INSEE
|
||||
85320,MAREUIL SUR LAY DISSAIS,FR,85135,46.5335825488,-1.22688907859,INSEE
|
||||
85320,PEAULT,FR,85171,46.502029199,-1.22708559855,INSEE
|
||||
85320,MAREUIL SUR LAY DISSAIS,FR,85135,46.5335825488,-1.22688907859,INSEE
|
||||
85320,LA COUTURE,FR,85074,46.523938732,-1.26493227292,INSEE
|
||||
85320,MOUTIERS SUR LE LAY,FR,85157,46.5651677306,-1.16826489836,INSEE
|
||||
85320,STE PEXINE,FR,85261,46.5596018797,-1.12406235901,INSEE
|
||||
@@ -275,7 +259,6 @@ EOF;
|
||||
85340,L ILE D OLONNE,FR,85112,46.570163703,-1.7737502368,INSEE
|
||||
85340,LES SABLES D OLONNE,FR,85194,46.5007612799,-1.79255128677,INSEE
|
||||
85350,L ILE D YEU,FR,85113,46.7093514816,-2.34712702345,INSEE
|
||||
85350,L ILE D YEU,FR,85113,46.7093514816,-2.34712702345,INSEE
|
||||
85360,LA TRANCHE SUR MER,FR,85294,46.3564601605,-1.43136322126,INSEE
|
||||
85370,MOUZEUIL ST MARTIN,FR,85158,46.4591118412,-0.984449849889,INSEE
|
||||
85370,NALLIERS,FR,85159,46.4658962703,-1.03958611312,INSEE
|
||||
@@ -286,7 +269,6 @@ EOF;
|
||||
85390,CHAVAGNES LES REDOUX,FR,85066,46.7101499475,-0.915900131393,INSEE
|
||||
85390,CHEFFOIS,FR,85067,46.6786935635,-0.782949851125,INSEE
|
||||
85390,MOUILLERON ST GERMAIN,FR,85154,46.6612700667,-0.846784201071,INSEE
|
||||
85390,MOUILLERON ST GERMAIN,FR,85154,46.6612700667,-0.846784201071,INSEE
|
||||
85400,STE GEMME LA PLAINE,FR,85216,46.4732196212,-1.11103084694,INSEE
|
||||
85400,LAIROUX,FR,85117,46.4496842668,-1.27114022202,INSEE
|
||||
85400,LUCON,FR,85128,46.4510564854,-1.16449285012,INSEE
|
||||
@@ -294,7 +276,6 @@ EOF;
|
||||
85400,CHASNAIS,FR,85058,46.4459908481,-1.2385924923,INSEE
|
||||
85410,ST LAURENT DE LA SALLE,FR,85237,46.5854041653,-0.922177315485,INSEE
|
||||
85410,LA CAILLERE ST HILAIRE,FR,85040,46.6293907412,-0.933153931505,INSEE
|
||||
85410,LA CAILLERE ST HILAIRE,FR,85040,46.6293907412,-0.933153931505,INSEE
|
||||
85410,ST CYR DES GATS,FR,85205,46.572397925,-0.86344873853,INSEE
|
||||
85410,THOUARSAIS BOUILDROUX,FR,85292,46.6062740621,-0.873461023865,INSEE
|
||||
85410,CEZAIS,FR,85041,46.5917363748,-0.802618133558,INSEE
|
||||
@@ -310,20 +291,16 @@ EOF;
|
||||
85420,RIVES D AUTISE,FR,85162,46.424726987,-0.665995249042,INSEE
|
||||
85430,AUBIGNY LES CLOUZEAUX,FR,85008,46.6028241769,-1.46743549114,INSEE
|
||||
85430,NIEUL LE DOLENT,FR,85161,46.5676509922,-1.51560194548,INSEE
|
||||
85430,AUBIGNY LES CLOUZEAUX,FR,85008,46.6028241769,-1.46743549114,INSEE
|
||||
85430,LA BOISSIERE DES LANDES,FR,85026,46.5581861734,-1.44371985689,INSEE
|
||||
85440,ST HILAIRE LA FORET,FR,85231,46.4551155186,-1.53048160541,INSEE
|
||||
85440,TALMONT ST HILAIRE,FR,85288,46.475786445,-1.62751498166,INSEE
|
||||
85440,POIROUX,FR,85179,46.5107890457,-1.53929317556,INSEE
|
||||
85440,GROSBREUIL,FR,85103,46.5390090882,-1.6072005484,INSEE
|
||||
85440,AVRILLE,FR,85010,46.4744272125,-1.49524360118,INSEE
|
||||
85440,TALMONT ST HILAIRE,FR,85288,46.475786445,-1.62751498166,INSEE
|
||||
85450,CHAMPAGNE LES MARAIS,FR,85049,46.3735020647,-1.13380723653,INSEE
|
||||
85450,LA TAILLEE,FR,85286,46.3852513569,-0.941017792066,INSEE
|
||||
85450,CHAILLE LES MARAIS,FR,85042,46.3853555319,-1.01044079362,INSEE
|
||||
85450,CHAILLE LES MARAIS,FR,85042,46.3853555319,-1.01044079362,INSEE
|
||||
85450,VOUILLE LES MARAIS,FR,85304,46.3891941167,-0.968106001439,INSEE
|
||||
85450,CHAILLE LES MARAIS,FR,85042,46.3853555319,-1.01044079362,INSEE
|
||||
85450,MOREILLES,FR,85149,46.4218721314,-1.09404530407,INSEE
|
||||
85450,PUYRAVAULT,FR,85185,46.3653834101,-1.09115660367,INSEE
|
||||
85450,STE RADEGONDE DES NOYERS,FR,85267,46.3694246909,-1.06671995264,INSEE
|
||||
@@ -331,14 +308,10 @@ EOF;
|
||||
85460,L AIGUILLON SUR MER,FR,85001,46.304138479,-1.2623239198,INSEE
|
||||
85470,BRETIGNOLLES SUR MER,FR,85035,46.6374826705,-1.86324200464,INSEE
|
||||
85470,BREM SUR MER,FR,85243,46.6118566989,-1.81003917923,INSEE
|
||||
85470,BREM SUR MER,FR,85243,46.6118566989,-1.81003917923,INSEE
|
||||
85480,BOURNEZEAU,FR,85034,46.6296975315,-1.14101742229,INSEE
|
||||
85480,FOUGERE,FR,85093,46.6617881911,-1.23612691916,INSEE
|
||||
85480,ST HILAIRE LE VOUHIS,FR,85232,46.6859198669,-1.15222590884,INSEE
|
||||
85480,THORIGNY,FR,85291,46.6179795025,-1.24888057642,INSEE
|
||||
85480,BOURNEZEAU,FR,85034,46.6296975315,-1.14101742229,INSEE
|
||||
85490,BENET,FR,85020,46.368873213,-0.613959918706,INSEE
|
||||
85490,BENET,FR,85020,46.368873213,-0.613959918706,INSEE
|
||||
85490,BENET,FR,85020,46.368873213,-0.613959918706,INSEE
|
||||
85500,BEAUREPAIRE,FR,85017,46.9050210355,-1.10144867013,INSEE
|
||||
85500,ST PAUL EN PAREDS,FR,85259,46.8303789022,-0.964515191283,INSEE
|
||||
@@ -356,12 +329,10 @@ EOF;
|
||||
85540,ST VINCENT SUR GRAON,FR,85277,46.5034756656,-1.382954206,INSEE
|
||||
85540,LA JONCHERE,FR,85116,46.4358647401,-1.38517843312,INSEE
|
||||
85540,ST BENOIST SUR MER,FR,85201,46.4266286403,-1.3399129604,INSEE
|
||||
85540,ST VINCENT SUR GRAON,FR,85277,46.5034756656,-1.382954206,INSEE
|
||||
85540,CURZON,FR,85077,46.4512376923,-1.30138059216,INSEE
|
||||
85540,ST AVAUGOURD DES LANDES,FR,85200,46.5136722903,-1.47528120789,INSEE
|
||||
85540,ST CYR EN TALMONDAIS,FR,85206,46.4597334032,-1.33722144355,INSEE
|
||||
85550,LA BARRE DE MONTS,FR,85012,46.8722784154,-2.09984018879,INSEE
|
||||
85550,LA BARRE DE MONTS,FR,85012,46.8722784154,-2.09984018879,INSEE
|
||||
85560,LE BERNARD,FR,85022,46.44832528,-1.43979865314,INSEE
|
||||
85560,LONGEVILLE SUR MER,FR,85127,46.4091029013,-1.47711855345,INSEE
|
||||
85570,POUILLE,FR,85181,46.5022256779,-0.955223380119,INSEE
|
||||
@@ -381,11 +352,7 @@ EOF;
|
||||
85590,ST MARS LA REORTHE,FR,85242,46.8597253005,-0.925777900202,INSEE
|
||||
85600,LA BOISSIERE DE MONTAIGU,FR,85025,46.9451858636,-1.1916392484,INSEE
|
||||
85600,MONTAIGU VENDEE,FR,85146,46.9759800852,-1.31364530268,INSEE
|
||||
85600,MONTAIGU VENDEE,FR,85146,46.9759800852,-1.31364530268,INSEE
|
||||
85600,TREIZE SEPTIERS,FR,85295,46.9975586143,-1.23193361154,INSEE
|
||||
85600,MONTAIGU VENDEE,FR,85146,46.9759800852,-1.31364530268,INSEE
|
||||
85600,MONTAIGU VENDEE,FR,85146,46.9759800852,-1.31364530268,INSEE
|
||||
85600,MONTAIGU VENDEE,FR,85146,46.9759800852,-1.31364530268,INSEE
|
||||
85610,CUGAND,FR,85076,47.0602388146,-1.25289811103,INSEE
|
||||
85610,LA BERNARDIERE,FR,85021,47.0361828072,-1.27390355206,INSEE
|
||||
85620,ROCHESERVIERE,FR,85190,46.9274273036,-1.50132208111,INSEE
|
||||
@@ -406,11 +373,8 @@ EOF;
|
||||
85700,MONTOURNAIS,FR,85147,46.7502937556,-0.770013941158,INSEE
|
||||
85700,POUZAUGES,FR,85182,46.7812581702,-0.828778359084,INSEE
|
||||
85700,REAUMUR,FR,85187,46.7145137269,-0.816742537248,INSEE
|
||||
85700,SEVREMONT,FR,85090,46.8211105864,-0.854584153953,INSEE
|
||||
85700,MENOMBLET,FR,85141,46.7301338667,-0.728654955878,INSEE
|
||||
85700,ST MESMIN,FR,85254,46.8005779435,-0.748892533741,INSEE
|
||||
85700,SEVREMONT,FR,85090,46.8211105864,-0.854584153953,INSEE
|
||||
85700,SEVREMONT,FR,85090,46.8211105864,-0.854584153953,INSEE
|
||||
85710,BOIS DE CENE,FR,85024,46.9479643351,-1.89668693466,INSEE
|
||||
85710,CHATEAUNEUF,FR,85062,46.916944435,-1.9261131832,INSEE
|
||||
85710,LA GARNACHE,FR,85096,46.8977541296,-1.82443040539,INSEE
|
||||
@@ -420,10 +384,9 @@ EOF;
|
||||
85770,VIX,FR,85303,46.3543018169,-0.856628326667,INSEE
|
||||
85770,LE GUE DE VELLUIRE,FR,85105,46.3675950645,-0.905432724485,INSEE
|
||||
85770,L ILE D ELLE,FR,85111,46.3334258655,-0.919100677098,INSEE
|
||||
85770,LES VELLUIRE SUR VENDEE,FR,85177,46.4190919441,-0.910475769222,INSEE
|
||||
85800,ST GILLES CROIX DE VIE,FR,85222,46.6904708814,-1.91946363327,INSEE
|
||||
85800,LE FENOUILLER,FR,85088,46.7161264566,-1.89206667498,INSEE
|
||||
85800,GIVRAND,FR,85100,46.6822701061,-1.8787272243,INSEE
|
||||
85800,GIVRAND,FR,85100,46.6822701061,-1.8787272243,INSEE
|
||||
EOF;
|
||||
|
||||
}
|
||||
|
@@ -20,7 +20,10 @@
|
||||
namespace Chill\MainBundle\DependencyInjection;
|
||||
|
||||
use Chill\MainBundle\Controller\AddressApiController;
|
||||
use Chill\MainBundle\Controller\LocationController;
|
||||
use Chill\MainBundle\Controller\LocationTypeController;
|
||||
use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonbArrayLength;
|
||||
use Chill\MainBundle\Doctrine\DQL\STContains;
|
||||
use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
@@ -44,6 +47,10 @@ use Chill\MainBundle\Doctrine\DQL\Replace;
|
||||
use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator;
|
||||
use Chill\MainBundle\Doctrine\Type\NativeDateIntervalType;
|
||||
use Chill\MainBundle\Doctrine\Type\PointType;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\LocationType;
|
||||
use Chill\MainBundle\Form\LocationTypeType;
|
||||
use Chill\MainBundle\Form\LocationFormType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
@@ -90,12 +97,19 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
// replace all config with a main key:
|
||||
$container->setParameter('chill_main', $config);
|
||||
|
||||
// legacy config
|
||||
$container->setParameter('chill_main.installation_name',
|
||||
$config['installation_name']);
|
||||
|
||||
$container->setParameter('chill_main.available_languages',
|
||||
$config['available_languages']);
|
||||
|
||||
$container->setParameter('chill_main.available_countries',
|
||||
$config['available_countries']);
|
||||
|
||||
$container->setParameter('chill_main.routing.resources',
|
||||
$config['routing']['resources']);
|
||||
|
||||
@@ -193,6 +207,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
'OVERLAPSI' => OverlapsI::class,
|
||||
'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class,
|
||||
'ST_CONTAINS' => STContains::class,
|
||||
'JSONB_ARRAY_LENGTH' => JsonbArrayLength::class,
|
||||
],
|
||||
],
|
||||
'hydrators' => [
|
||||
@@ -315,7 +330,51 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
'template' => '@ChillMain/User/edit.html.twig'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'class' => Location::class,
|
||||
'name' => 'main_location',
|
||||
'base_path' => '/admin/main/location',
|
||||
'base_role' => 'ROLE_ADMIN',
|
||||
'form_class' => LocationFormType::class,
|
||||
'controller' => LocationController::class,
|
||||
'actions' => [
|
||||
'index' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/Location/index.html.twig',
|
||||
],
|
||||
'new' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/Location/new.html.twig',
|
||||
],
|
||||
'edit' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/Location/edit.html.twig',
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'class' => LocationType::class,
|
||||
'name' => 'main_location_type',
|
||||
'base_path' => '/admin/main/location-type',
|
||||
'base_role' => 'ROLE_ADMIN',
|
||||
'form_class' => LocationTypeType::class,
|
||||
'controller' => LocationTypeController::class,
|
||||
'actions' => [
|
||||
'index' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/LocationType/index.html.twig',
|
||||
],
|
||||
'new' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/LocationType/new.html.twig',
|
||||
],
|
||||
'edit' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/LocationType/edit.html.twig',
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'apis' => [
|
||||
[
|
||||
@@ -470,6 +529,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
],
|
||||
[
|
||||
'class' => \Chill\MainBundle\Entity\LocationType::class,
|
||||
'controller' => \Chill\MainBundle\Controller\LocationTypeApiController::class,
|
||||
'name' => 'location_type',
|
||||
'base_path' => '/api/1.0/main/location-type',
|
||||
'base_role' => 'ROLE_USER',
|
||||
|
@@ -1,35 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Chill\MainBundle\Form\PermissionsGroupType;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class ACLFlagsCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$permissionGroupType = $container->getDefinition(PermissionsGroupType::class);
|
||||
|
||||
|
||||
foreach($container->findTaggedServiceIds('chill_main.flags') as $id => $tags) {
|
||||
$reference = new Reference($id);
|
||||
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
switch($tag['scope']) {
|
||||
case PermissionsGroupType::FLAG_SCOPE:
|
||||
|
||||
|
||||
$permissionGroupType->addMethodCall('addFlagProvider', [ $reference ]);
|
||||
break;
|
||||
default:
|
||||
throw new \LogicalException(sprintf(
|
||||
throw new LogicException(sprintf(
|
||||
"This tag 'scope' is not implemented: %s, on service with id %s", $tag['scope'], $id)
|
||||
);
|
||||
}
|
||||
|
@@ -16,23 +16,20 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
|
||||
use AddWidgetConfigurationTrait;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var ContainerBuilder
|
||||
*/
|
||||
private $containerBuilder;
|
||||
|
||||
|
||||
public function __construct(array $widgetFactories = array(),
|
||||
use AddWidgetConfigurationTrait;
|
||||
|
||||
private ContainerBuilder $containerBuilder;
|
||||
|
||||
|
||||
public function __construct(
|
||||
array $widgetFactories,
|
||||
ContainerBuilder $containerBuilder)
|
||||
{
|
||||
$this->setWidgetFactories($widgetFactories);
|
||||
$this->containerBuilder = $containerBuilder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -51,6 +48,10 @@ class Configuration implements ConfigurationInterface
|
||||
->defaultValue(array('fr'))
|
||||
->prototype('scalar')->end()
|
||||
->end() // end of array 'available_languages'
|
||||
->arrayNode('available_countries')
|
||||
->defaultValue(array('FR'))
|
||||
->prototype('scalar')->end()
|
||||
->end() // end of array 'available_countries'
|
||||
->arrayNode('routing')
|
||||
->children()
|
||||
->arrayNode('resources')
|
||||
@@ -97,6 +98,14 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('acl')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('form_show_scopes')
|
||||
->defaultTrue()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('redis')
|
||||
->children()
|
||||
->scalarNode('host')
|
||||
@@ -247,7 +256,7 @@ class Configuration implements ConfigurationInterface
|
||||
->end() // end of root
|
||||
;
|
||||
|
||||
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
|
@@ -30,27 +30,27 @@ use Chill\MainBundle\DependencyInjection\Widget\HasWidgetFactoriesExtensionInter
|
||||
|
||||
/**
|
||||
* Compile the configurations and inject required service into container.
|
||||
*
|
||||
*
|
||||
* The widgets are services tagged with :
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* { name: chill_widget, alias: my_alias, place: my_place }
|
||||
* ```
|
||||
*
|
||||
* Or, if the tag does not exist or if you need to add some config to your
|
||||
*
|
||||
* Or, if the tag does not exist or if you need to add some config to your
|
||||
* service depending on the config, you should use a `WidgetFactory` (see
|
||||
* `WidgetFactoryInterface`.
|
||||
*
|
||||
* To reuse this compiler pass, simple execute the doProcess metho in your
|
||||
*
|
||||
* To reuse this compiler pass, simple execute the doProcess metho in your
|
||||
* compiler. Example :
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* namespace Chill\MainBundle\DependencyInjection\CompilerPass;
|
||||
*
|
||||
*
|
||||
* use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
* use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass;
|
||||
* class WidgetsCompilerPass extends AbstractWidgetsCompilerPass {
|
||||
*
|
||||
*
|
||||
* public function process(ContainerBuilder $container)
|
||||
* {
|
||||
* $this->doProcess($container, 'chill_main', 'chill_main.widgets');
|
||||
@@ -58,58 +58,58 @@ use Chill\MainBundle\DependencyInjection\Widget\HasWidgetFactoriesExtensionInter
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
private $widgetServices = array();
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var WidgetFactoryInterface[]
|
||||
*/
|
||||
private $widgetFactories;
|
||||
|
||||
|
||||
/**
|
||||
* The service which will manage the widgets
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const WIDGET_MANAGER = 'chill.main.twig.widget';
|
||||
|
||||
|
||||
/**
|
||||
* the method wich register the widget into give service.
|
||||
*/
|
||||
const WIDGET_MANAGER_METHOD_REGISTER = 'addWidget';
|
||||
|
||||
|
||||
/**
|
||||
* the value of the `name` key in service definitions's tag
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const WIDGET_SERVICE_TAG_NAME = 'chill_widget';
|
||||
|
||||
|
||||
/**
|
||||
* the key used to collect the alias in the service definition's tag.
|
||||
* the alias must be
|
||||
* the key used to collect the alias in the service definition's tag.
|
||||
* the alias must be
|
||||
* injected into the configuration under 'alias' key.
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const WIDGET_SERVICE_TAG_ALIAS = 'alias';
|
||||
|
||||
|
||||
/**
|
||||
* the key used to collect the authorized place in the service definition's tag
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const WIDGET_SERVICE_TAG_PLACES = 'place';
|
||||
|
||||
|
||||
/**
|
||||
* the key to use to order widget for a given place
|
||||
*/
|
||||
const WIDGET_CONFIG_ORDER = 'order';
|
||||
|
||||
|
||||
/**
|
||||
* the key to use to identify widget for a given place
|
||||
*/
|
||||
@@ -118,24 +118,25 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
|
||||
/**
|
||||
* process the configuration and the container to add the widget available
|
||||
*
|
||||
*
|
||||
* @param ContainerBuilder $container
|
||||
* @param string $extension the extension of your bundle
|
||||
* @param string $containerWidgetConfigParameterName the key under which we can use the widget configuration
|
||||
* @throws \LogicException
|
||||
* @throws \UnexpectedValueException if the given extension does not implement HasWidgetExtensionInterface
|
||||
* @throws \InvalidConfigurationException if there are errors in the config
|
||||
*/
|
||||
public function doProcess(ContainerBuilder $container, $extension,
|
||||
$containerWidgetConfigParameterName)
|
||||
{
|
||||
public function doProcess(
|
||||
ContainerBuilder $container,
|
||||
$extension,
|
||||
$containerWidgetConfigParameterName
|
||||
) {
|
||||
if (!$container->hasDefinition(self::WIDGET_MANAGER)) {
|
||||
throw new \LogicException("the service ".self::WIDGET_MANAGER." should".
|
||||
" be present. It is required by ".self::class);
|
||||
}
|
||||
|
||||
|
||||
$managerDefinition = $container->getDefinition(self::WIDGET_MANAGER);
|
||||
|
||||
|
||||
// collect the widget factories
|
||||
/* @var $extensionClass HasWidgetFactoriesExtensionInterface */
|
||||
$extensionClass = $container->getExtension($extension);
|
||||
@@ -148,19 +149,19 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
HasWidgetFactoriesExtensionInterface::class,
|
||||
get_class($extensionClass)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$this->widgetFactories = $extensionClass->getWidgetFactories();
|
||||
|
||||
|
||||
// collect the availabled tagged services
|
||||
$this->collectTaggedServices($container);
|
||||
|
||||
|
||||
// collect the widgets and their config :
|
||||
$widgetParameters = $container->getParameter($containerWidgetConfigParameterName);
|
||||
|
||||
|
||||
// and add them to the delegated_block
|
||||
foreach($widgetParameters as $place => $widgets) {
|
||||
|
||||
|
||||
foreach ($widgets as $param) {
|
||||
$alias = $param[self::WIDGET_CONFIG_ALIAS];
|
||||
// check that the service exists
|
||||
@@ -168,43 +169,43 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
throw new InvalidConfigurationException(sprintf("The alias %s".
|
||||
" is not defined.", $alias));
|
||||
}
|
||||
|
||||
|
||||
// check that the widget is allowed at this place
|
||||
if (!$this->isPlaceAllowedForWidget($place, $alias, $container)) {
|
||||
throw new \InvalidConfigurationException(sprintf(
|
||||
throw new InvalidConfigurationException(sprintf(
|
||||
"The widget with alias %s is not allowed at place %s",
|
||||
$alias,
|
||||
$alias,
|
||||
$place
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// get the order, eventually corrected
|
||||
$order = $this->cacheAndGetOrdering($place, $param[self::WIDGET_CONFIG_ORDER]);
|
||||
|
||||
$order = $this->cacheAndGetOrdering($place, $param[self::WIDGET_CONFIG_ORDER]);
|
||||
|
||||
// register the widget with config to the service, using the method
|
||||
// `addWidget`
|
||||
if ($this->widgetServices[$alias] instanceof WidgetFactoryInterface) {
|
||||
/* @var $factory WidgetFactoryInterface */
|
||||
$factory = $this->widgetServices[$alias];
|
||||
// get the config (under the key which equals to widget_alias
|
||||
$config = isset($param[$factory->getWidgetAlias()]) ?
|
||||
$config = isset($param[$factory->getWidgetAlias()]) ?
|
||||
$param[$factory->getWidgetAlias()] : array();
|
||||
// register the service into the container
|
||||
$serviceId =$this->registerServiceIntoContainer($container,
|
||||
$serviceId =$this->registerServiceIntoContainer($container,
|
||||
$factory, $place, $order, $config);
|
||||
|
||||
|
||||
$managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER,
|
||||
array(
|
||||
$place,
|
||||
$order,
|
||||
new Reference($serviceId),
|
||||
$place,
|
||||
$order,
|
||||
new Reference($serviceId),
|
||||
$config
|
||||
));
|
||||
} else {
|
||||
$managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER,
|
||||
array(
|
||||
$place,
|
||||
$order,
|
||||
$place,
|
||||
$order,
|
||||
new Reference($this->widgetServices[$alias]),
|
||||
array() // the config is alway an empty array
|
||||
));
|
||||
@@ -212,10 +213,10 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* register the service into container.
|
||||
*
|
||||
*
|
||||
* @param ContainerBuilder $container
|
||||
* @param WidgetFactoryInterface $factory
|
||||
* @param string $place
|
||||
@@ -231,28 +232,28 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
array $config
|
||||
) {
|
||||
$serviceId = $factory->getServiceId($container, $place, $order, $config);
|
||||
$definition = $factory->createDefinition($container, $place,
|
||||
$definition = $factory->createDefinition($container, $place,
|
||||
$order, $config);
|
||||
$container->setDefinition($serviceId, $definition);
|
||||
|
||||
|
||||
return $serviceId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cache of ordering by place.
|
||||
*
|
||||
*
|
||||
* @internal used by function cacheAndGetOrdering
|
||||
* @var array
|
||||
*/
|
||||
private $cacheOrdering = array();
|
||||
|
||||
|
||||
/**
|
||||
* check if the ordering has already be used for the given $place and,
|
||||
* if yes, correct the ordering by incrementation of 1 until the ordering
|
||||
* has not be used.
|
||||
*
|
||||
*
|
||||
* recursive method.
|
||||
*
|
||||
*
|
||||
* @param string $place
|
||||
* @param float $ordering
|
||||
* @return float
|
||||
@@ -262,7 +263,7 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
if (!array_key_exists($place, $this->cacheOrdering)) {
|
||||
$this->cacheOrdering[$place] = array();
|
||||
}
|
||||
|
||||
|
||||
// check if the order exists
|
||||
if (array_search($ordering, $this->cacheOrdering[$place])) {
|
||||
// if the order exists, increment of 1 and try again
|
||||
@@ -270,14 +271,14 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
} else {
|
||||
// cache the ordering
|
||||
$this->cacheOrdering[$place][] = $ordering;
|
||||
|
||||
|
||||
return $ordering;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the places where the service is allowed
|
||||
*
|
||||
*
|
||||
* @param Definition $definition
|
||||
* @return unknown
|
||||
*/
|
||||
@@ -288,7 +289,7 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
->getAllowedPlaces())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
$definition = $container->findDefinition($this->widgetServices[$widgetAlias]);
|
||||
|
||||
@@ -300,17 +301,17 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method collect all service tagged with `self::WIDGET_SERVICE_TAG`, and
|
||||
* add also the widget defined by factories
|
||||
*
|
||||
*
|
||||
* This method also check that the service is correctly tagged with `alias` and
|
||||
* `places`, or the factory give a correct alias and more than one place.
|
||||
*
|
||||
*
|
||||
* @param ContainerBuilder $container
|
||||
* @throws InvalidConfigurationException
|
||||
* @throws InvalidArgumentException
|
||||
@@ -320,13 +321,13 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
// first, check the service tagged in service definition
|
||||
foreach ($container->findTaggedServiceIds(self::WIDGET_SERVICE_TAG_NAME) as $id => $attrs) {
|
||||
foreach ($attrs as $attr) {
|
||||
|
||||
|
||||
// check the alias is set
|
||||
if (!isset($attr[self::WIDGET_SERVICE_TAG_ALIAS])) {
|
||||
throw new InvalidConfigurationException("you should add an ".self::WIDGET_SERVICE_TAG_ALIAS.
|
||||
" key on the service ".$id);
|
||||
}
|
||||
|
||||
|
||||
// check the place is set
|
||||
if (!isset($attr[self::WIDGET_SERVICE_TAG_PLACES])) {
|
||||
throw new InvalidConfigurationException(sprintf(
|
||||
@@ -335,54 +336,54 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
|
||||
$id
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// check the alias does not exists yet
|
||||
if (array_key_exists($attr[self::WIDGET_SERVICE_TAG_ALIAS], $this->widgetServices)) {
|
||||
throw new InvalidArgumentException("a service has already be defined with the ".
|
||||
self::WIDGET_SERVICE_TAG_ALIAS." ".$attr[self::WIDGET_SERVICE_TAG_ALIAS]);
|
||||
}
|
||||
|
||||
|
||||
// register the service as available
|
||||
$this->widgetServices[$attr[self::WIDGET_SERVICE_TAG_ALIAS]] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add the services defined by factories
|
||||
foreach($this->widgetFactories as $factory) {
|
||||
/* @var $factory WidgetFactoryInterface */
|
||||
$alias = $factory->getWidgetAlias();
|
||||
|
||||
|
||||
// check the alias is not empty
|
||||
if (empty($alias)) {
|
||||
throw new \LogicException(sprintf(
|
||||
"the widget factory %s returns an empty alias",
|
||||
get_class($factory)));
|
||||
}
|
||||
|
||||
|
||||
// check the places are not empty
|
||||
if (!is_array($factory->getAllowedPlaces())) {
|
||||
throw new \UnexpectedValueException("the method 'getAllowedPlaces' "
|
||||
. "should return a non-empty array. Unexpected value on ".
|
||||
get_class($factory));
|
||||
}
|
||||
|
||||
|
||||
if (count($factory->getAllowedPlaces()) == 0) {
|
||||
throw new \LengthException("The method 'getAllowedPlaces' should "
|
||||
. "return a non-empty array, but returned 0 elements on ".
|
||||
get_class($factory).'::getAllowedPlaces()');
|
||||
}
|
||||
|
||||
|
||||
// check the alias does not exists yet
|
||||
if (array_key_exists($alias, $this->widgetServices)) {
|
||||
throw new InvalidArgumentException("a service has already be defined with the ".
|
||||
self::WIDGET_SERVICE_TAG_ALIAS." ".$alias);
|
||||
}
|
||||
|
||||
|
||||
// register the factory as available
|
||||
$this->widgetServices[$factory->getWidgetAlias()] = $factory;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
35
src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbArrayLength.php
Normal file
35
src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbArrayLength.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
*
|
||||
*/
|
||||
namespace Chill\MainBundle\Doctrine\DQL;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
|
||||
/**
|
||||
* Return the length of an array
|
||||
*/
|
||||
class JsonbArrayLength extends FunctionNode
|
||||
{
|
||||
private $expr1;
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return sprintf(
|
||||
'jsonb_array_length(%s)',
|
||||
$this->expr1->dispatch($sqlWalker),
|
||||
);
|
||||
}
|
||||
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$this->expr1 = $parser->StringPrimary();
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
namespace Chill\MainBundle\Doctrine\DQL;
|
||||
|
||||
@@ -11,19 +11,18 @@ use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
@author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class JsonbExistsInArray extends FunctionNode
|
||||
{
|
||||
private $expr1;
|
||||
private $expr2;
|
||||
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return sprintf(
|
||||
'jsonb_exists(%s, %s)',
|
||||
'%s ?? %s',
|
||||
$this->expr1->dispatch($sqlWalker),
|
||||
$sqlWalker->walkInputParameter($this->expr2)
|
||||
);
|
||||
|
@@ -4,13 +4,9 @@ namespace Chill\MainBundle\Doctrine\Model;
|
||||
|
||||
use \JsonSerializable;
|
||||
|
||||
/**
|
||||
* Description of Point
|
||||
*
|
||||
*/
|
||||
class Point implements JsonSerializable {
|
||||
private ?float $lat = null;
|
||||
private ?float $lon = null;
|
||||
private ?float $lat;
|
||||
private ?float $lon;
|
||||
public static string $SRID = '4326';
|
||||
|
||||
private function __construct(?float $lon, ?float $lat)
|
||||
@@ -22,6 +18,7 @@ class Point implements JsonSerializable {
|
||||
public function toGeoJson(): string
|
||||
{
|
||||
$array = $this->toArrayGeoJson();
|
||||
|
||||
return \json_encode($array);
|
||||
}
|
||||
|
||||
@@ -33,60 +30,53 @@ class Point implements JsonSerializable {
|
||||
public function toArrayGeoJson(): array
|
||||
{
|
||||
return [
|
||||
"type" => "Point",
|
||||
"coordinates" => [ $this->lon, $this->lat ]
|
||||
'type' => 'Point',
|
||||
'coordinates' => [$this->lon, $this->lat],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toWKT(): string
|
||||
{
|
||||
return 'SRID='.self::$SRID.';POINT('.$this->lon.' '.$this->lat.')';
|
||||
return sprintf("SRID=%s;POINT(%s %s)", self::$SRID, $this->lon, $this->lat);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type $geojson
|
||||
* @return Point
|
||||
*/
|
||||
public static function fromGeoJson(string $geojson): Point
|
||||
public static function fromGeoJson(string $geojson): self
|
||||
{
|
||||
$a = json_decode($geojson);
|
||||
//check if the geojson string is correct
|
||||
if (NULL === $a or !isset($a->type) or !isset($a->coordinates)){
|
||||
|
||||
if (null === $a) {
|
||||
throw PointException::badJsonString($geojson);
|
||||
}
|
||||
|
||||
if ($a->type != 'Point'){
|
||||
if (null === $a->type || null === $a->coordinates) {
|
||||
throw PointException::badJsonString($geojson);
|
||||
}
|
||||
|
||||
if ($a->type !== 'Point'){
|
||||
throw PointException::badGeoType();
|
||||
}
|
||||
|
||||
$lat = $a->coordinates[1];
|
||||
$lon = $a->coordinates[0];
|
||||
[$lon, $lat] = $a->coordinates;
|
||||
|
||||
return Point::fromLonLat($lon, $lat);
|
||||
}
|
||||
|
||||
public static function fromLonLat(float $lon, float $lat): Point
|
||||
public static function fromLonLat(float $lon, float $lat): self
|
||||
{
|
||||
if (($lon > -180 && $lon < 180) && ($lat > -90 && $lat < 90))
|
||||
{
|
||||
if (($lon > -180 && $lon < 180) && ($lat > -90 && $lat < 90)) {
|
||||
return new Point($lon, $lat);
|
||||
} else {
|
||||
throw PointException::badCoordinates($lon, $lat);
|
||||
}
|
||||
|
||||
throw PointException::badCoordinates($lon, $lat);
|
||||
}
|
||||
|
||||
public static function fromArrayGeoJson(array $array): Point
|
||||
public static function fromArrayGeoJson(array $array): self
|
||||
{
|
||||
if ($array['type'] == 'Point' &&
|
||||
isset($array['coordinates']))
|
||||
{
|
||||
if ($array['type'] === 'Point' && isset($array['coordinates'])) {
|
||||
return self::fromLonLat($array['coordinates'][0], $array['coordinates'][1]);
|
||||
}
|
||||
|
||||
throw new \Exception('Unable to build a point from input data.');
|
||||
}
|
||||
|
||||
public function getLat(): float
|
||||
|
@@ -15,17 +15,17 @@ use Doctrine\DBAL\Types\ConversionException;
|
||||
class NativeDateIntervalType extends DateIntervalType
|
||||
{
|
||||
const FORMAT = '%rP%YY%MM%DDT%HH%IM%SS';
|
||||
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return \Doctrine\DBAL\Types\Type::DATEINTERVAL;
|
||||
return \Doctrine\DBAL\Types\Types::DATEINTERVAL;
|
||||
}
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
|
||||
{
|
||||
return 'INTERVAL';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -41,36 +41,36 @@ class NativeDateIntervalType extends DateIntervalType
|
||||
|
||||
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateInterval']);
|
||||
}
|
||||
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
if ($value === null || $value instanceof \DateInterval) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$strings = explode(' ', $value);
|
||||
|
||||
|
||||
if (count($strings) === 0) {
|
||||
return null;
|
||||
}
|
||||
$intervalSpec = 'P';
|
||||
\reset($strings);
|
||||
|
||||
|
||||
do {
|
||||
$intervalSpec .= $this->convertEntry($strings);
|
||||
} while (next($strings) !== FALSE);
|
||||
|
||||
|
||||
return new \DateInterval($intervalSpec);
|
||||
} catch (\Exception $exception) {
|
||||
throw $this->createConversionException($value, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function convertEntry(&$strings)
|
||||
{
|
||||
$current = \current($strings);
|
||||
|
||||
|
||||
if (is_numeric($current)) {
|
||||
$next = \next($strings);
|
||||
switch($next) {
|
||||
@@ -79,7 +79,7 @@ class NativeDateIntervalType extends DateIntervalType
|
||||
$unit = 'Y';
|
||||
break;
|
||||
case 'mon':
|
||||
case 'mons':
|
||||
case 'mons':
|
||||
$unit = 'M';
|
||||
break;
|
||||
case 'day':
|
||||
@@ -89,20 +89,20 @@ class NativeDateIntervalType extends DateIntervalType
|
||||
default:
|
||||
throw $this->createConversionException(implode('', $strings));
|
||||
}
|
||||
|
||||
|
||||
return $current.$unit;
|
||||
|
||||
|
||||
} elseif (\preg_match('/([0-9]{2}\:[0-9]{2}:[0-9]{2})/', $current) === 1) {
|
||||
$tExploded = explode(':', $current);
|
||||
$intervalSpec = 'T';
|
||||
$intervalSpec.= $tExploded[0].'H';
|
||||
$intervalSpec.= $tExploded[1].'M';
|
||||
$intervalSpec.= $tExploded[2].'S';
|
||||
|
||||
|
||||
return $intervalSpec;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function createConversionException($value, $exception = null)
|
||||
{
|
||||
return ConversionException::conversionFailedFormat($value, $this->getName(), 'xx year xx mons xx days 01:02:03', $exception);
|
||||
|
@@ -28,7 +28,7 @@ class Country
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="json_array")
|
||||
* @ORM\Column(type="json")
|
||||
* @groups({"read"})
|
||||
*
|
||||
*/
|
||||
|
@@ -43,14 +43,14 @@ class Language
|
||||
/**
|
||||
* @var string array
|
||||
*
|
||||
* @ORM\Column(type="json_array")
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return string
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
@@ -59,7 +59,7 @@ class Language
|
||||
|
||||
/**
|
||||
* Set id
|
||||
*
|
||||
*
|
||||
* @param string $id
|
||||
* @return Language
|
||||
*/
|
||||
@@ -78,17 +78,17 @@ class Language
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name
|
||||
*
|
||||
* @return string array
|
||||
* @return string array
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -76,6 +76,12 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
|
||||
*/
|
||||
private bool $availableForUsers = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", nullable=true)
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
private bool $active = true;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=User::class)
|
||||
* @Serializer\Groups({"read"})
|
||||
@@ -192,6 +198,18 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActive(): ?bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): self
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedBy(): ?User
|
||||
{
|
||||
return $this->createdBy;
|
||||
|
@@ -40,6 +40,12 @@ class LocationType
|
||||
*/
|
||||
private bool $availableForUsers = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", nullable=true)
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
private bool $active = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=32, options={"default"="optional"})
|
||||
* @Serializer\Groups({"read"})
|
||||
@@ -70,6 +76,18 @@ class LocationType
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActive(): ?bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): self
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAvailableForUsers(): ?bool
|
||||
{
|
||||
return $this->availableForUsers;
|
||||
|
@@ -52,7 +52,7 @@ class Scope
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @ORM\Column(type="json_array")
|
||||
* @ORM\Column(type="json")
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private $name = [];
|
||||
|
@@ -116,7 +116,7 @@ class User implements AdvancedUserInterface {
|
||||
* Array where SAML attributes's data are stored
|
||||
* @var array
|
||||
*
|
||||
* @ORM\Column(type="json_array", nullable=true)
|
||||
* @ORM\Column(type="json", nullable=true)
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
|
@@ -3,10 +3,14 @@
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table("chill_main_user_job")
|
||||
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "user_job"=UserJob::class
|
||||
* })
|
||||
*/
|
||||
class UserJob
|
||||
{
|
||||
@@ -15,12 +19,14 @@ class UserJob
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
protected ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var array|string[]A
|
||||
* @ORM\Column(name="label", type="json")
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
protected array $label = [];
|
||||
|
||||
|
85
src/Bundle/ChillMainBundle/Form/LocationFormType.php
Normal file
85
src/Bundle/ChillMainBundle/Form/LocationFormType.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\LocationType as EntityLocationType;
|
||||
use Chill\MainBundle\Form\Type\PickAddressType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class LocationFormType extends AbstractType
|
||||
{
|
||||
|
||||
// private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
// /**
|
||||
// * @param TranslatableStringHelper $translatableStringHelper
|
||||
// */
|
||||
// public function __construct(TranslatableStringHelper $translatableStringHelper)
|
||||
// {
|
||||
// $this->translatableStringHelper = $translatableStringHelper;
|
||||
// }
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
|
||||
$builder
|
||||
->add('locationType', EntityType::class, [
|
||||
'class' => EntityLocationType::class,
|
||||
'choice_attr' => function (EntityLocationType $entity) {
|
||||
return [
|
||||
'data-address' => $entity->getAddressRequired(),
|
||||
'data-contact' => $entity->getContactData(),
|
||||
];
|
||||
},
|
||||
'choice_label' => function (EntityLocationType $entity) {
|
||||
//return $this->translatableStringHelper->localize($entity->getTitle()); //TODO not working. Cannot pass smthg in the constructor
|
||||
return $entity->getTitle()['fr'];
|
||||
},
|
||||
])
|
||||
->add('name', TextType::class)
|
||||
->add('phonenumber1', TextType::class, ['required' => false])
|
||||
->add('phonenumber2', TextType::class, ['required' => false])
|
||||
->add('email', TextType::class, ['required' => false])
|
||||
->add('address', PickAddressType::class, [
|
||||
'required' => false,
|
||||
'label' => 'Address',
|
||||
'use_valid_from' => false,
|
||||
'use_valid_to' => false,
|
||||
'mapped' => false,
|
||||
])
|
||||
->add('active', ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'Yes' => true,
|
||||
'No' => false
|
||||
],
|
||||
'expanded' => true
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param OptionsResolverInterface $resolver
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults(array(
|
||||
'data_class' => 'Chill\MainBundle\Entity\Location'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'chill_mainbundle_location';
|
||||
}
|
||||
|
||||
}
|
54
src/Bundle/ChillMainBundle/Form/LocationTypeType.php
Normal file
54
src/Bundle/ChillMainBundle/Form/LocationTypeType.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\LocationType;
|
||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final class LocationTypeType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->add('title', TranslatableStringFormType::class,
|
||||
[
|
||||
'label' => 'Name',
|
||||
])
|
||||
->add('availableForUsers', ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'Yes' => true,
|
||||
'No' => false
|
||||
],
|
||||
'expanded' => true
|
||||
])
|
||||
->add('addressRequired', ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'optional' => LocationType::STATUS_OPTIONAL,
|
||||
'required' => LocationType::STATUS_REQUIRED,
|
||||
'never' => LocationType::STATUS_NEVER,
|
||||
],
|
||||
'expanded' => true
|
||||
])
|
||||
->add('contactData', ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'optional' => LocationType::STATUS_OPTIONAL,
|
||||
'required' => LocationType::STATUS_REQUIRED,
|
||||
'never' => LocationType::STATUS_NEVER,
|
||||
],
|
||||
'expanded' => true
|
||||
])
|
||||
->add('active', ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'Yes' => true,
|
||||
'No' => false
|
||||
],
|
||||
'expanded' => true
|
||||
]);
|
||||
}
|
||||
}
|
@@ -54,7 +54,7 @@ class CommentType extends AbstractType
|
||||
$data = $event->getForm()->getData();
|
||||
$comment = $event->getData() ?? ['comment' => ''];
|
||||
|
||||
if ($data->getComment() !== $comment['comment']) {
|
||||
if (null !== $data && $data->getComment() !== $comment['comment']) {
|
||||
$data->setDate(new \DateTime());
|
||||
$data->setUserId($this->user->getId());
|
||||
$event->getForm()->setData($data);
|
||||
|
@@ -51,8 +51,10 @@ class CenterTransformer implements DataTransformerInterface
|
||||
}
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
if ($this->multiple) {
|
||||
$ids = \explode(',', $id);
|
||||
$ids = explode(',', $id);
|
||||
} else {
|
||||
$ids[] = (int) $id;
|
||||
}
|
||||
@@ -68,9 +70,9 @@ class CenterTransformer implements DataTransformerInterface
|
||||
|
||||
if ($this->multiple) {
|
||||
return new ArrayCollection($centers);
|
||||
} else {
|
||||
return $centers[0];
|
||||
}
|
||||
|
||||
return $centers[0];
|
||||
}
|
||||
|
||||
public function transform($center)
|
||||
|
@@ -3,9 +3,12 @@
|
||||
namespace Chill\MainBundle\Form\Type\Listing;
|
||||
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SearchType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
final class FilterOrderType extends \Symfony\Component\Form\AbstractType
|
||||
@@ -29,9 +32,32 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType
|
||||
]);
|
||||
}
|
||||
|
||||
$checkboxesBuilder = $builder->create('checkboxes', null, [ 'compound' => true ]);
|
||||
foreach ($helper->getCheckboxes() as $name => $c) {
|
||||
|
||||
$choices = \array_combine(
|
||||
\array_map(function($c, $t) {
|
||||
if ($t !== NULL) { return $t; }
|
||||
else { return $c; }
|
||||
}, $c['choices'], $c['trans']),
|
||||
$c['choices']
|
||||
);
|
||||
|
||||
$checkboxesBuilder->add($name, ChoiceType::class, [
|
||||
'choices' => $choices,
|
||||
'expanded' => true,
|
||||
'multiple' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
if (0 < count($helper->getCheckboxes())) {
|
||||
$builder->add($checkboxesBuilder);
|
||||
}
|
||||
|
||||
foreach ($this->requestStack->getCurrentRequest()->query->getIterator() as $key => $value) {
|
||||
switch($key) {
|
||||
case 'q':
|
||||
case 'checkboxes'.$key:
|
||||
break;
|
||||
case 'page':
|
||||
$builder->add($key, HiddenType::class, [
|
||||
@@ -47,6 +73,17 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType
|
||||
}
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
/** @var FilterOrderHelper $helper */
|
||||
$helper = $options['helper'];
|
||||
$view->vars['has_search_box'] = $helper->hasSearchBox();
|
||||
$view->vars['checkboxes'] = [];
|
||||
foreach ($helper->getCheckboxes() as $name => $c) {
|
||||
$view->vars['checkboxes'][$name] = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(\Symfony\Component\OptionsResolver\OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setRequired('helper')
|
||||
|
@@ -23,7 +23,9 @@ use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
@@ -36,6 +38,7 @@ use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
/**
|
||||
* Allow to pick amongst available scope for the current
|
||||
@@ -46,14 +49,10 @@ use Symfony\Component\Security\Core\Role\Role;
|
||||
* - `center`: the center of the entity
|
||||
* - `role` : the role of the user
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class ScopePickerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
protected AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
@@ -70,22 +69,26 @@ class ScopePickerType extends AbstractType
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
|
||||
protected Security $security;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
AuthorizationHelperInterface $authorizationHelper,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
ScopeRepository $scopeRepository,
|
||||
Security $security,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
) {
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
$this->security = $security;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$query = $this->buildAccessibleScopeQuery($options['center'], $options['role']);
|
||||
$items = $query->getQuery()->execute();
|
||||
$items = $this->authorizationHelper->getReachableScopes($this->security->getUser(),
|
||||
$options['role'], $options['center']);
|
||||
|
||||
if (1 !== count($items)) {
|
||||
$builder->add('scope', EntityType::class, [
|
||||
@@ -94,9 +97,7 @@ class ScopePickerType extends AbstractType
|
||||
'choice_label' => function (Scope $c) {
|
||||
return $this->translatableStringHelper->localize($c->getName());
|
||||
},
|
||||
'query_builder' => function () use ($options) {
|
||||
return $this->buildAccessibleScopeQuery($options['center'], $options['role']);
|
||||
},
|
||||
'choices' => $items,
|
||||
]);
|
||||
$builder->setDataMapper(new ScopePickerDataMapper());
|
||||
} else {
|
||||
@@ -121,19 +122,22 @@ class ScopePickerType extends AbstractType
|
||||
$resolver
|
||||
// create `center` option
|
||||
->setRequired('center')
|
||||
->setAllowedTypes('center', [Center::class])
|
||||
->setAllowedTypes('center', [Center::class, 'array', 'null'])
|
||||
// create ``role` option
|
||||
->setRequired('role')
|
||||
->setAllowedTypes('role', ['string', Role::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Center|array|Center[] $center
|
||||
* @param string $role
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
protected function buildAccessibleScopeQuery(Center $center, Role $role)
|
||||
protected function buildAccessibleScopeQuery($center, $role)
|
||||
{
|
||||
$roles = $this->authorizationHelper->getParentRoles($role);
|
||||
$roles[] = $role;
|
||||
$centers = $center instanceof Center ? [$center]: $center;
|
||||
|
||||
$qb = $this->scopeRepository->createQueryBuilder('s');
|
||||
$qb
|
||||
@@ -142,8 +146,8 @@ class ScopePickerType extends AbstractType
|
||||
->join('rs.permissionsGroups', 'pg')
|
||||
->join('pg.groupCenters', 'gc')
|
||||
// add center constraint
|
||||
->where($qb->expr()->eq('IDENTITY(gc.center)', ':center'))
|
||||
->setParameter('center', $center->getId())
|
||||
->where($qb->expr()->in('IDENTITY(gc.center)', ':centers'))
|
||||
->setParameter('centers', \array_map(fn(Center $c) => $c->getId(), $centers))
|
||||
// role constraints
|
||||
->andWhere($qb->expr()->in('rs.role', ':roles'))
|
||||
->setParameter('roles', $roles)
|
||||
|
@@ -28,6 +28,7 @@ use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\Form\Type\Select2ChoiceType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
/**
|
||||
* Extends choice to allow adding select2 library on widget
|
||||
@@ -37,31 +38,25 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
*/
|
||||
class Select2CountryType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var RequestStack
|
||||
*/
|
||||
private $requestStack;
|
||||
private RequestStack $requestStack;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
private ObjectManager $em;
|
||||
|
||||
/**
|
||||
* @var ObjectManager
|
||||
*/
|
||||
private $em;
|
||||
protected TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
protected ParameterBagInterface $parameterBag;
|
||||
|
||||
public function __construct(
|
||||
RequestStack $requestStack,
|
||||
ObjectManager $em,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
ParameterBagInterface $parameterBag
|
||||
)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->em = $em;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->parameterBag = $parameterBag;
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
@@ -82,19 +77,29 @@ class Select2CountryType extends AbstractType
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$locale = $this->requestStack->getCurrentRequest()->getLocale();
|
||||
$countries = $this->em->getRepository('Chill\MainBundle\Entity\Country')->findAll();
|
||||
$choices = array();
|
||||
$choices = [];
|
||||
$preferredCountries = $this->parameterBag->get('chill_main.available_countries');
|
||||
$preferredChoices = [];
|
||||
|
||||
foreach ($countries as $c) {
|
||||
$choices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
|
||||
}
|
||||
|
||||
foreach ($preferredCountries as $pc) {
|
||||
foreach ($countries as $c) {
|
||||
if ($c->getCountryCode() == $pc) {
|
||||
$preferredChoices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asort($choices, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
||||
$resolver->setDefaults(array(
|
||||
'class' => 'Chill\MainBundle\Entity\Country',
|
||||
'choices' => array_combine(array_values($choices),array_keys($choices))
|
||||
'choices' => array_combine(array_values($choices),array_keys($choices)),
|
||||
'preferred_choices' => array_combine(array_values($preferredChoices), array_keys($preferredChoices))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@@ -28,37 +28,32 @@ use Chill\MainBundle\Form\Type\DataTransformer\MultipleObjectsToIdTransformer;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\Form\Type\Select2ChoiceType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
/**
|
||||
* Extends choice to allow adding select2 library on widget for languages (multiple)
|
||||
*/
|
||||
class Select2LanguageType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var RequestStack
|
||||
*/
|
||||
private $requestStack;
|
||||
private RequestStack $requestStack;
|
||||
|
||||
/**
|
||||
* @var ObjectManager
|
||||
*/
|
||||
private $em;
|
||||
private ObjectManager $em;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
protected TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
protected ParameterBagInterface $parameterBag;
|
||||
|
||||
public function __construct(
|
||||
RequestStack $requestStack,
|
||||
ObjectManager $em,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
ParameterBagInterface $parameterBag
|
||||
)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->em = $em;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->parameterBag = $parameterBag;
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
@@ -79,19 +74,24 @@ class Select2LanguageType extends AbstractType
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$locale = $this->requestStack->getCurrentRequest()->getLocale();
|
||||
$languages = $this->em->getRepository('Chill\MainBundle\Entity\Language')->findAll();
|
||||
$choices = array();
|
||||
$preferredLanguages = $this->parameterBag->get('chill_main.available_languages');
|
||||
$choices = [];
|
||||
$preferredChoices = [];
|
||||
|
||||
foreach ($languages as $l) {
|
||||
$choices[$l->getId()] = $this->translatableStringHelper->localize($l->getName());
|
||||
}
|
||||
foreach ($preferredLanguages as $l) {
|
||||
$preferredChoices[$l] = $choices[$l];
|
||||
}
|
||||
|
||||
asort($choices, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
||||
$resolver->setDefaults(array(
|
||||
'class' => 'Chill\MainBundle\Entity\Language',
|
||||
'choices' => array_combine(array_values($choices),array_keys($choices))
|
||||
'choices' => array_combine(array_values($choices), array_keys($choices)),
|
||||
'preferred_choices' => array_combine(array_values($preferredChoices), array_keys($preferredChoices))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
namespace Chill\MainBundle\Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
@@ -56,14 +58,18 @@ class UserPickerType extends AbstractType
|
||||
|
||||
protected UserRepository $userRepository;
|
||||
|
||||
protected UserACLAwareRepositoryInterface $userACLAwareRepository;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
UserRepository $userRepository
|
||||
UserRepository $userRepository,
|
||||
UserACLAwareRepositoryInterface $userACLAwareRepository
|
||||
) {
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->userRepository = $userRepository;
|
||||
$this->userACLAwareRepository = $userACLAwareRepository;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +78,7 @@ class UserPickerType extends AbstractType
|
||||
$resolver
|
||||
// create `center` option
|
||||
->setRequired('center')
|
||||
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class ])
|
||||
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class, 'null', 'array' ])
|
||||
// create ``role` option
|
||||
->setRequired('role')
|
||||
->setAllowedTypes('role', ['string', \Symfony\Component\Security\Core\Role\Role::class ])
|
||||
@@ -86,17 +92,19 @@ class UserPickerType extends AbstractType
|
||||
->setDefault('choice_label', function(User $u) {
|
||||
return $u->getUsername();
|
||||
})
|
||||
->setDefault('scope', null)
|
||||
->setAllowedTypes('scope', [Scope::class, 'array', 'null'])
|
||||
->setNormalizer('choices', function(Options $options) {
|
||||
|
||||
$users = $this->authorizationHelper
|
||||
->findUsersReaching($options['role'], $options['center']);
|
||||
|
||||
|
||||
$users = $this->userACLAwareRepository
|
||||
->findUsersByReachedACL($options['role'], $options['center'], $options['scope'], true);
|
||||
|
||||
if (NULL !== $options['having_permissions_group_flag']) {
|
||||
return $this->userRepository
|
||||
->findUsersHavingFlags($options['having_permissions_group_flag'], $users)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
return $users;
|
||||
})
|
||||
;
|
||||
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\ParentRoleHelper;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class UserACLAwareRepository implements UserACLAwareRepositoryInterface
|
||||
{
|
||||
private ParentRoleHelper $parentRoleHelper;
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
public function __construct(ParentRoleHelper $parentRoleHelper, EntityManagerInterface $em)
|
||||
{
|
||||
$this->parentRoleHelper = $parentRoleHelper;
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function findUsersByReachedACL(string $role, $center, $scope = null, bool $onlyEnabled = true): array
|
||||
{
|
||||
$parents = $this->parentRoleHelper->getParentRoles($role);
|
||||
$parents[] = $role;
|
||||
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->select('u')
|
||||
->from(User::class, 'u')
|
||||
->join('u.groupCenters', 'gc')
|
||||
->join('gc.permissionsGroup', 'pg')
|
||||
->join('pg.roleScopes', 'rs')
|
||||
->where($qb->expr()->in('rs.role', $parents))
|
||||
;
|
||||
|
||||
if ($onlyEnabled) {
|
||||
$qb->andWhere($qb->expr()->eq('u.enabled', "'TRUE'"));
|
||||
}
|
||||
|
||||
if (NULL !== $center) {
|
||||
$centers = $center instanceof Center ? [$center] : $center;
|
||||
$qb
|
||||
->andWhere($qb->expr()->in('gc.center', ':centers'))
|
||||
->setParameter('centers', $centers)
|
||||
;
|
||||
}
|
||||
|
||||
if (NULL !== $scope) {
|
||||
$scopes = $scope instanceof Scope ? [$scope] : $scope;
|
||||
$qb
|
||||
->andWhere($qb->expr()->in('rs.scope', ':scopes'))
|
||||
->setParameter('scopes', $scopes)
|
||||
;
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
|
||||
interface UserACLAwareRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find the users reaching the given center and scope, for the given role
|
||||
*
|
||||
* @param string $role
|
||||
* @param Center|Center[]|array $center
|
||||
* @param Scope|Scope[]|array|null $scope
|
||||
* @param bool $onlyActive true if get only active users
|
||||
*
|
||||
* @return User[]
|
||||
*/
|
||||
public function findUsersByReachedACL(string $role, $center, $scope = null, bool $onlyActive = true): array;
|
||||
}
|
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\GroupCenter;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
@@ -48,6 +49,24 @@ final class UserRepository implements ObjectRepository
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function countBy(array $criteria): int
|
||||
{
|
||||
return $this->repository->count($criteria);
|
||||
}
|
||||
|
||||
public function countByActive(): int
|
||||
{
|
||||
return $this->countBy(['enabled' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User[]|array
|
||||
*/
|
||||
public function findByActive(array $orderBy = null, int $limit = null, int $offset = null): array
|
||||
{
|
||||
return $this->findBy(['enabled' => true], $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function getClassName() {
|
||||
return User::class;
|
||||
}
|
||||
|
@@ -1 +1,5 @@
|
||||
require("./show_hide.js");
|
||||
//require("./show_hide.js");
|
||||
|
||||
import { ShowHide } from './show_hide.js'
|
||||
|
||||
export { ShowHide }
|
@@ -134,4 +134,4 @@ var ShowHide = function(options) {
|
||||
};
|
||||
};
|
||||
|
||||
export {ShowHide};
|
||||
export { ShowHide };
|
||||
|
@@ -0,0 +1,13 @@
|
||||
.confidential{
|
||||
display: flex;
|
||||
}
|
||||
.toggle{
|
||||
margin-left: 30px;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.blur {
|
||||
-webkit-filter: blur(5px);
|
||||
-moz-filter: blur(5px);
|
||||
filter: blur(5px);
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
require('./blur.scss');
|
||||
|
||||
var toggleBlur = function(e){
|
||||
|
||||
var btn = e.target;
|
||||
|
||||
btn.previousElementSibling.classList.toggle("blur");
|
||||
btn.classList.toggle("fa-eye");
|
||||
btn.classList.toggle("fa-eye-slash");
|
||||
|
||||
}
|
||||
|
||||
var infos = document.getElementsByClassName("confidential");
|
||||
for(var i=0; i < infos.length; i++){
|
||||
infos[i].insertAdjacentHTML('beforeend', '<i class="fa fa-eye toggle" aria-hidden="true"></i>');
|
||||
}
|
||||
|
||||
var toggles = document.getElementsByClassName("toggle");
|
||||
for(var i=0; i < toggles.length; i++){
|
||||
toggles[i].addEventListener("click", toggleBlur);
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
const contactDataBlock = document.querySelector('div.location-form-contact');
|
||||
const addressBlock = document.querySelector('div.location-form-address');
|
||||
const locationType = document.getElementById('chill_mainbundle_location_locationType');
|
||||
|
||||
const getSelectedAttributes =
|
||||
(select, attr) => select.selectedOptions[0].getAttribute(attr)
|
||||
|
||||
|
||||
const removeRequired = (formBlock) => {
|
||||
formBlock.querySelectorAll('label').forEach(
|
||||
l => l.classList.remove('required')
|
||||
);
|
||||
formBlock.querySelectorAll('input').forEach(
|
||||
i => i.removeAttribute('required')
|
||||
);
|
||||
}
|
||||
|
||||
const addRequired = (formBlock) => {
|
||||
formBlock.querySelectorAll('label').forEach(
|
||||
l => l.classList.add('required')
|
||||
);
|
||||
formBlock.querySelectorAll('input').forEach(
|
||||
i => i.setAttribute('required', '')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const onLocationTypeChange = () => {
|
||||
console.log(getSelectedAttributes(locationType, 'data-address'))
|
||||
console.log(getSelectedAttributes(locationType, 'data-contact'))
|
||||
switch (getSelectedAttributes(locationType, 'data-address')) {
|
||||
case 'optional':
|
||||
default:
|
||||
removeRequired(addressBlock);
|
||||
addressBlock.classList.remove('d-none');
|
||||
break;
|
||||
case 'required':
|
||||
addRequired(addressBlock);
|
||||
addressBlock.classList.remove('d-none');
|
||||
break;
|
||||
case 'never':
|
||||
removeRequired(addressBlock);
|
||||
addressBlock.classList.add('d-none');
|
||||
break;
|
||||
}
|
||||
switch (getSelectedAttributes(locationType, 'data-contact')) {
|
||||
case 'optional':
|
||||
default:
|
||||
removeRequired(contactDataBlock);
|
||||
contactDataBlock.classList.remove('d-none');
|
||||
break;
|
||||
case 'required':
|
||||
addRequired(contactDataBlock);
|
||||
contactDataBlock.classList.remove('d-none');
|
||||
break;
|
||||
case 'never':
|
||||
removeRequired(contactDataBlock);
|
||||
contactDataBlock.classList.add('d-none');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', _e => {
|
||||
onLocationTypeChange();
|
||||
locationType.addEventListener('change', onLocationTypeChange);
|
||||
});
|
@@ -68,7 +68,9 @@ export default {
|
||||
return this.$data.value !== null && typeof this.$data.value.text !== 'undefined';
|
||||
},
|
||||
cities() {
|
||||
return this.entity.loaded.cities;
|
||||
return this.entity.loaded.cities.sort(
|
||||
(a, b) => Number(a.code) - Number(b.code) || a.name > b.name
|
||||
)
|
||||
},
|
||||
name: {
|
||||
set(value) {
|
||||
@@ -92,7 +94,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
transName(value) {
|
||||
return (value.code && value.name) ? `${value.code}-${value.name}` : '';
|
||||
return (value.code && value.name) ? `${value.name} (${value.code})` : '';
|
||||
},
|
||||
selectCity(value) {
|
||||
console.log(value)
|
||||
@@ -103,7 +105,9 @@ export default {
|
||||
console.log('writeNew.postcode false, in selectCity');
|
||||
this.$emit('getReferenceAddresses', value);
|
||||
this.focusOnAddress();
|
||||
this.updateMapCenter(value.center);
|
||||
if (value.center) {
|
||||
this.updateMapCenter(value.center);
|
||||
}
|
||||
},
|
||||
listenInputSearch(query) {
|
||||
//console.log('listenInputSearch', query, this.isCitySelectorOpen);
|
||||
|
@@ -5,82 +5,89 @@ import App from './App.vue';
|
||||
|
||||
const i18n = _createI18n(addressMessages);
|
||||
|
||||
let inputs = document.querySelectorAll('input[type="hidden"][data-input-address]');
|
||||
const addAddressInput = (inputs) => {
|
||||
|
||||
const isNumeric = function(v) { return !isNaN(v); };
|
||||
inputs.forEach(el => {
|
||||
let
|
||||
addressId = el.value,
|
||||
uniqid = el.dataset.inputAddress,
|
||||
container = document.querySelector('div[data-input-address-container="' + uniqid + '"]'),
|
||||
isEdit = addressId !== '',
|
||||
addressIdInt = addressId !== '' ? parseInt(addressId) : null
|
||||
;
|
||||
|
||||
inputs.forEach(el => {
|
||||
let
|
||||
addressId = el.value,
|
||||
uniqid = el.dataset.inputAddress,
|
||||
container = document.querySelector('div[data-input-address-container="' + uniqid + '"]'),
|
||||
isEdit = addressId !== '',
|
||||
addressIdInt = addressId !== '' ? parseInt(addressId) : null
|
||||
;
|
||||
if (container === null) {
|
||||
throw Error("no container");
|
||||
}
|
||||
console.log('useValidFrom', el.dataset.useValidFrom === '1');
|
||||
|
||||
if (container === null) {
|
||||
throw Error("no container");
|
||||
}
|
||||
console.log('useValidFrom', el.dataset.useValidFrom === '1');
|
||||
|
||||
const app = createApp({
|
||||
template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
|
||||
data() {
|
||||
return {
|
||||
addAddress: {
|
||||
context: {
|
||||
// for legacy ? can be remove ?
|
||||
target: {
|
||||
name: 'input-address',
|
||||
id: addressIdInt,
|
||||
},
|
||||
edit: isEdit,
|
||||
addressId: addressIdInt,
|
||||
},
|
||||
options: {
|
||||
/// Options override default.
|
||||
/// null value take default component value defined in AddAddress data()
|
||||
button: {
|
||||
text: {
|
||||
create: el.dataset.buttonTextCreate || null,
|
||||
edit: el.dataset.buttonTextUpdate || null,
|
||||
const app = createApp({
|
||||
template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
|
||||
data() {
|
||||
return {
|
||||
addAddress: {
|
||||
context: {
|
||||
// for legacy ? can be remove ?
|
||||
target: {
|
||||
name: 'input-address',
|
||||
id: addressIdInt,
|
||||
},
|
||||
size: null,
|
||||
displayText: true
|
||||
edit: isEdit,
|
||||
addressId: addressIdInt,
|
||||
},
|
||||
options: {
|
||||
/// Options override default.
|
||||
/// null value take default component value defined in AddAddress data()
|
||||
button: {
|
||||
text: {
|
||||
create: el.dataset.buttonTextCreate || null,
|
||||
edit: el.dataset.buttonTextUpdate || null,
|
||||
},
|
||||
size: null,
|
||||
displayText: true
|
||||
},
|
||||
|
||||
/// Modal title text if create or edit address (trans chain, see i18n)
|
||||
title: {
|
||||
create: null,
|
||||
edit: null,
|
||||
},
|
||||
/// Modal title text if create or edit address (trans chain, see i18n)
|
||||
title: {
|
||||
create: null,
|
||||
edit: null,
|
||||
},
|
||||
|
||||
/// Display panes in Modal for step123
|
||||
openPanesInModal: true,
|
||||
/// Display panes in Modal for step123
|
||||
openPanesInModal: true,
|
||||
|
||||
/// Display actions buttons of panes in a sticky-form-button navbar
|
||||
stickyActions: false,
|
||||
showMessageWhenNoAddress: true,
|
||||
/// Display actions buttons of panes in a sticky-form-button navbar
|
||||
stickyActions: false,
|
||||
showMessageWhenNoAddress: true,
|
||||
|
||||
/// Use Date fields
|
||||
useDate: {
|
||||
validFrom: el.dataset.useValidFrom === '1' || false, //boolean, default: false
|
||||
validTo: el.dataset.useValidTo === '1' || false, //boolean, default: false
|
||||
},
|
||||
/// Use Date fields
|
||||
useDate: {
|
||||
validFrom: el.dataset.useValidFrom === '1' || false, //boolean, default: false
|
||||
validTo: el.dataset.useValidTo === '1' || false, //boolean, default: false
|
||||
},
|
||||
|
||||
/// Don't display show renderbox Address: showPane display only a button
|
||||
onlyButton: false,
|
||||
/// Don't display show renderbox Address: showPane display only a button
|
||||
onlyButton: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
associateToInput(payload) {
|
||||
el.value = payload.addressId;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
associateToInput(payload) {
|
||||
el.value = payload.addressId;
|
||||
}
|
||||
}
|
||||
})
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount(container);
|
||||
});
|
||||
})
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount(container);
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', (_e) =>
|
||||
addAddressInput(document.querySelectorAll('input[type="hidden"][data-input-address]'))
|
||||
);
|
||||
|
||||
window.addEventListener('collection-add-entry', (e) =>
|
||||
addAddressInput(e.detail.entry.querySelectorAll('input[type="hidden"][data-input-address]'))
|
||||
);
|
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="confidential" v-on:click="toggleBlur">
|
||||
<div class="confidential-content blur">
|
||||
<slot name="confidential-content"></slot>
|
||||
</div>
|
||||
<i class="fa fa-eye toggle" aria-hidden="true"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Confidential",
|
||||
methods : {
|
||||
toggleBlur: function(e){
|
||||
if(e.target.matches('.toggle')){
|
||||
console.log(e);
|
||||
e.target.previousElementSibling.classList.toggle("blur");
|
||||
e.target.classList.toggle("fa-eye");
|
||||
e.target.classList.toggle("fa-eye-slash");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang='scss'>
|
||||
.confidential{
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.toggle{
|
||||
margin-top: 28px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.blur {
|
||||
-webkit-filter: blur(5px);
|
||||
-moz-filter: blur(5px);
|
||||
filter: blur(5px);
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<span class="chill-entity entity-user">
|
||||
{{ user.label }}
|
||||
<span class="user-job" v-if="user.user_job !== null">({{ user.user_job.label.fr }})</span>
|
||||
<span class="main-scope" v-if="user.main_scope !== null">({{ user.main_scope.name.fr }})</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "UserRenderBoxBadge",
|
||||
props: ['user'],
|
||||
}
|
||||
</script>
|
@@ -39,3 +39,4 @@ assetic:
|
||||
|
||||
chill_main:
|
||||
available_languages: [fr, en]
|
||||
available_countries: [FR]
|
||||
|
@@ -0,0 +1,33 @@
|
||||
{#
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
|
||||
|
||||
{% block vertical_menu_content %}
|
||||
{{ chill_menu('admin_location', {
|
||||
'layout': '@ChillMain/Admin/menu_admin_location.html.twig',
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block layout_wvm_content %}
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Management of location' |trans }}</h1>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@@ -0,0 +1,20 @@
|
||||
{#
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillMain/Menu/verticalMenu.html.twig" %}
|
||||
{% block v_menu_title %}{{ 'Location Menu'|trans }}{% endblock %}
|
@@ -58,6 +58,13 @@
|
||||
|
||||
{% macro inline(address, options) %}
|
||||
{% if options['has_no_address'] == true and address.isNoAddress == true %}
|
||||
{% if address.postCode is not empty %}
|
||||
<p class="postcode">
|
||||
<span class="code">{{ address.postCode.code }}</span>
|
||||
<span class="name">{{ address.postCode.name }}</span>
|
||||
</p>
|
||||
<p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p>
|
||||
{% endif %}
|
||||
<span class="noaddress">
|
||||
{{ 'address.consider homeless'|trans }}
|
||||
</span>
|
||||
@@ -83,6 +90,10 @@
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
this enclose the rendering inside a "li", which ease the placement operation when the address
|
||||
must be shown in such list
|
||||
#}
|
||||
{%- if render == 'list' -%}
|
||||
<li class="chill-entity entity-address">
|
||||
{% if options['with_picto'] %}
|
||||
@@ -104,9 +115,19 @@
|
||||
{%- if render == 'bloc' -%}
|
||||
<div class="chill-entity entity-address">
|
||||
{% if options['has_no_address'] == true and address.isNoAddress == true %}
|
||||
{% if address.postCode is not empty %}
|
||||
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">
|
||||
<p class="postcode">
|
||||
<span class="code">{{ address.postCode.code }}</span>
|
||||
<span class="name">{{ address.postCode.name }}</span>
|
||||
</p>
|
||||
<p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="noaddress">
|
||||
{{ 'address.consider homeless'|trans }}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">
|
||||
{% if options['with_picto'] %}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
<span class="chill-entity entity-user">
|
||||
{{- user.label }}
|
||||
{%- if opts['user_job'] and user.userJob is not null %}
|
||||
<span class="user-job">({{ user.userJob.label|localize_translatable_string }})</span>
|
||||
{%- endif -%}
|
||||
{%- if opts['main_scope'] and user.mainScope is not null %}
|
||||
<span class="main-scope">({{ user.mainScope.name|localize_translatable_string }})</span>
|
||||
{%- endif -%}
|
||||
</span>
|
@@ -1,12 +1,39 @@
|
||||
{{ form_start(form) }}
|
||||
<div class="chill_filter_order container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="input-group mb-3">
|
||||
{{ form_widget(form.q)}}
|
||||
<button type="submit" class="btn btn-chill-l-gray"><i class="fa fa-search"></i></button>
|
||||
{% if form.vars.has_search_box %}
|
||||
<div class="col-md-12">
|
||||
<div class="input-group mb-3">
|
||||
{{ form_widget(form.q)}}
|
||||
<button type="submit" class="btn btn-chill-l-gray"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if form.checkboxes|length > 0 %}
|
||||
{% for checkbox_name, options in form.checkboxes %}
|
||||
<div class="row gx-0">
|
||||
<div class="col-md-12">
|
||||
{% for c in form['checkboxes'][checkbox_name].children %}
|
||||
<div class="form-check form-check-inline">
|
||||
{{ form_widget(c) }}
|
||||
{{ form_label(c) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% if loop.last %}
|
||||
<div class="row gx-0">
|
||||
<div class="col-md-12">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<button type="submit" class="btn btn-misc"><i class="fa fa-filter"></i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
|
@@ -0,0 +1,39 @@
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
|
||||
|
||||
{% block crud_content_form_rows %}
|
||||
|
||||
{{ form_row(form.locationType) }}
|
||||
|
||||
<div class="location-form-address">
|
||||
{{ form_row(form.address) }}
|
||||
</div>
|
||||
|
||||
{{ form_row(form.name) }}
|
||||
|
||||
<div class="location-form-contact">
|
||||
{{ form_row(form.phonenumber1) }}
|
||||
{{ form_row(form.phonenumber2) }}
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
|
||||
{% endblock crud_content_form_rows %}
|
||||
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('mod_input_address') }}
|
||||
{{ encore_entry_script_tags('page_location') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('mod_input_address') }}
|
||||
{% endblock %}
|
@@ -0,0 +1,56 @@
|
||||
{% extends "@ChillMain/Admin/layout_location.html.twig" %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Location list'|trans }}</h1>
|
||||
|
||||
<table class="records_list table table-bordered border-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'Name'|trans }}</th>
|
||||
<th>{{ 'Phonenumber1'|trans }}</th>
|
||||
<th>{{ 'Phonenumber2'|trans }}</th>
|
||||
<th>{{ 'Email'|trans }}</th>
|
||||
<th>{{ 'Address'|trans }}</th>
|
||||
<th>{{ 'Active'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.name }}</td>
|
||||
<td>{{ entity.phonenumber1 }}</td>
|
||||
<td>{{ entity.phonenumber2 }}</td>
|
||||
<td>{{ entity.email }}</td>
|
||||
<td>
|
||||
{% if entity.address is not null %}
|
||||
{{ entity.address.street}}, {{ entity.address.streetnumber }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
{%- if entity.active -%}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
{%- else -%}
|
||||
<i class="fa fa-square-o"></i>
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_main_location_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_main_location_new') }}" class="btn btn-create">
|
||||
{{ 'Create a new location'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
@@ -0,0 +1,39 @@
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
|
||||
|
||||
{% block crud_content_form_rows %}
|
||||
|
||||
{{ form_row(form.locationType) }}
|
||||
|
||||
<div class="location-form-address">
|
||||
{{ form_row(form.address) }}
|
||||
</div>
|
||||
|
||||
{{ form_row(form.name) }}
|
||||
|
||||
<div class="location-form-contact">
|
||||
{{ form_row(form.phonenumber1) }}
|
||||
{{ form_row(form.phonenumber2) }}
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
|
||||
{% endblock crud_content_form_rows %}
|
||||
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('mod_input_address') }}
|
||||
{{ encore_entry_script_tags('page_location') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('mod_input_address') }}
|
||||
{% endblock %}
|
@@ -0,0 +1,14 @@
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{# {% as we are in the admin layout, we override the admin content with the CRUD content %} #}
|
||||
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
|
||||
{# we do not have "view" page. We empty the corresponding block #}
|
||||
{% block content_form_actions_view %}{% endblock %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
@@ -0,0 +1,55 @@
|
||||
{% extends "@ChillMain/Admin/layout_location.html.twig" %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h1>{{ 'Location type list'|trans }}</h1>
|
||||
|
||||
<table class="records_list table table-bordered border-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'Title'|trans }}</th>
|
||||
<th>{{ 'Available for users'|trans }}</th>
|
||||
<th>{{ 'Address required'|trans }}</th>
|
||||
<th>{{ 'Contact data'|trans }}</th>
|
||||
<th>{{ 'Active'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.title | localize_translatable_string }}</td>
|
||||
<td style="text-align:center;">
|
||||
{%- if entity.availableForUsers -%}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
{%- else -%}
|
||||
<i class="fa fa-square-o"></i>
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td>{{ entity.addressRequired|trans }}</td>
|
||||
<td>{{ entity.contactData|trans }}</td>
|
||||
<td style="text-align:center;">
|
||||
{%- if entity.active -%}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
{%- else -%}
|
||||
<i class="fa fa-square-o"></i>
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_main_location_type_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_main_location_type_new') }}" class="btn btn-create">
|
||||
{{ 'Create a new location type'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock %}
|
@@ -0,0 +1,11 @@
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
@@ -8,7 +8,7 @@
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path(cancel_route, cancel_parameters|default( { } ) ) }}" class="btn btn-cancel">
|
||||
{{ 'Cancel'|trans }}
|
||||
@@ -18,5 +18,5 @@
|
||||
{{ form_widget(form.submit, { 'attr' : { 'class' : "btn btn-delete" } } ) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
@@ -12,7 +12,8 @@
|
||||
{{ encore_entry_link_tags('mod_forkawesome') }}
|
||||
{{ encore_entry_link_tags('mod_ckeditor5') }}
|
||||
{{ encore_entry_link_tags('chill') }}
|
||||
{% block css%}<!-- nothing added to css -->{% endblock %}
|
||||
{{ encore_entry_link_tags('mod_blur') }}
|
||||
{% block css %}<!-- nothing added to css -->{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -88,6 +89,7 @@
|
||||
{{ encore_entry_script_tags('mod_bootstrap') }}
|
||||
{{ encore_entry_script_tags('mod_forkawesome') }}
|
||||
{{ encore_entry_script_tags('mod_ckeditor5') }}
|
||||
{{ encore_entry_script_tags('mod_blur') }}
|
||||
{{ encore_entry_script_tags('chill') }}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@@ -54,6 +54,15 @@ class AdminSectionMenuBuilder implements LocalMenuBuilderInterface
|
||||
'order' => 200,
|
||||
'explain' => "Configure permissions for users"
|
||||
]);
|
||||
|
||||
$menu->addChild('Location and location type', [
|
||||
'route' => 'chill_main_admin_locations'
|
||||
])
|
||||
->setExtras([
|
||||
'icons' => ['key'],
|
||||
'order' => 205,
|
||||
'explain' => "Configure location and location type"
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Routing\MenuBuilder;
|
||||
|
||||
use Knp\Menu\MenuItem;
|
||||
|
||||
class LocationMenuBuilder implements \Chill\MainBundle\Routing\LocalMenuBuilderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return [ 'admin_location' ];
|
||||
}
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
$menu->addChild('Location type list', [
|
||||
'route' => 'chill_crud_main_location_type_index'
|
||||
])->setExtras(['order' => 205]);
|
||||
|
||||
$menu->addChild('Location list', [
|
||||
'route' => 'chill_crud_main_location_index'
|
||||
])->setExtras(['order' => 206]);
|
||||
}
|
||||
}
|
@@ -139,7 +139,7 @@ class SearchApi
|
||||
return $nq->getResult();
|
||||
}
|
||||
|
||||
private function prepareProviders($rawResults)
|
||||
private function prepareProviders(array $rawResults)
|
||||
{
|
||||
$metadatas = [];
|
||||
foreach ($rawResults as $r) {
|
||||
@@ -156,8 +156,10 @@ class SearchApi
|
||||
}
|
||||
}
|
||||
|
||||
private function buildResults($rawResults)
|
||||
private function buildResults(array $rawResults): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($rawResults as $r) {
|
||||
foreach ($this->providers as $k => $p) {
|
||||
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
||||
@@ -170,6 +172,6 @@ class SearchApi
|
||||
}
|
||||
}
|
||||
|
||||
return $items ?? [];
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,9 @@ use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Repository\UserACLAwareRepository;
|
||||
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
|
||||
use Chill\MainBundle\Security\ParentRoleHelper;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
|
||||
@@ -44,38 +47,28 @@ use Chill\MainBundle\Entity\RoleScope;
|
||||
*/
|
||||
class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
{
|
||||
protected RoleHierarchyInterface $roleHierarchy;
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
|
||||
/**
|
||||
* The role in a hierarchy, given by the parameter
|
||||
* `security.role_hierarchy.roles` from the container.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $hierarchy;
|
||||
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
protected EntityManagerInterface $em;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
protected CenterResolverDispatcher $centerResolverDispatcher;
|
||||
private UserACLAwareRepositoryInterface $userACLAwareRepository;
|
||||
|
||||
protected ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
private ParentRoleHelper $parentRoleHelper;
|
||||
|
||||
public function __construct(
|
||||
RoleHierarchyInterface $roleHierarchy,
|
||||
ParameterBagInterface $parameterBag,
|
||||
EntityManagerInterface $em,
|
||||
CenterResolverDispatcher $centerResolverDispatcher,
|
||||
LoggerInterface $logger,
|
||||
ScopeResolverDispatcher $scopeResolverDispatcher
|
||||
ScopeResolverDispatcher $scopeResolverDispatcher,
|
||||
UserACLAwareRepositoryInterface $userACLAwareRepository,
|
||||
ParentRoleHelper $parentRoleHelper
|
||||
) {
|
||||
$this->roleHierarchy = $roleHierarchy;
|
||||
$this->hierarchy = $parameterBag->get('security.role_hierarchy.roles');
|
||||
$this->em = $em;
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
$this->logger = $logger;
|
||||
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
|
||||
$this->userACLAwareRepository = $userACLAwareRepository;
|
||||
$this->parentRoleHelper = $parentRoleHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +80,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
* @param Center|Center[] $center May be an array of center
|
||||
* @return bool
|
||||
*/
|
||||
public function userCanReachCenter(User $user, $center)
|
||||
public function userCanReachCenter(User $user, $center): bool
|
||||
{
|
||||
if ($center instanceof \Traversable) {
|
||||
foreach ($center as $c) {
|
||||
@@ -320,71 +313,40 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated use UserACLAwareRepositoryInterface::findUsersByReachedACL instead
|
||||
* @param Center|Center[]|array $center
|
||||
* @param Scope|Scope[]|array|null $scope
|
||||
* @param bool $onlyActive true if get only active users
|
||||
* @return User[]
|
||||
*/
|
||||
public function findUsersReaching(string $role, Center $center, Scope $circle = null): array
|
||||
public function findUsersReaching(string $role, $center, $scope = null, bool $onlyEnabled = true): array
|
||||
{
|
||||
$parents = $this->getParentRoles($role);
|
||||
$parents[] = $role;
|
||||
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$qb
|
||||
->select('u')
|
||||
->from(User::class, 'u')
|
||||
->join('u.groupCenters', 'gc')
|
||||
->join('gc.permissionsGroup', 'pg')
|
||||
->join('pg.roleScopes', 'rs')
|
||||
->where('gc.center = :center')
|
||||
->andWhere($qb->expr()->in('rs.role', $parents))
|
||||
;
|
||||
|
||||
$qb->setParameter('center', $center);
|
||||
|
||||
if ($circle !== null) {
|
||||
$qb->andWhere('rs.scope = :circle')
|
||||
->setParameter('circle', $circle)
|
||||
;
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
return $this->userACLAwareRepository
|
||||
->findUsersByReachedACL($role, $center, $scope, $onlyEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a parent role may give access to a given child role
|
||||
*
|
||||
* @param Role $childRole The role we want to test if he is reachable
|
||||
* @param Role $parentRole The role which should give access to $childRole
|
||||
* @param string $childRole The role we want to test if he is reachable
|
||||
* @param string $parentRole The role which should give access to $childRole
|
||||
* @return boolean true if the child role is granted by parent role
|
||||
*/
|
||||
protected function isRoleReached($childRole, $parentRole)
|
||||
private function isRoleReached(string $childRole, string $parentRole)
|
||||
{
|
||||
$reachableRoles = $this->roleHierarchy
|
||||
->getReachableRoleNames([$parentRole]);
|
||||
|
||||
return in_array($childRole, $reachableRoles);
|
||||
return $this->parentRoleHelper->isRoleReached($childRole, $parentRole);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the role which give access to the given role. Only the role
|
||||
* which are registered into Chill are taken into account.
|
||||
*
|
||||
* @param Role $role
|
||||
* @param string $role
|
||||
* @return string[] the role which give access to the given $role
|
||||
*/
|
||||
public function getParentRoles($role): array
|
||||
public function getParentRoles(string $role): array
|
||||
{
|
||||
$parentRoles = [];
|
||||
// transform the roles from role hierarchy from string to Role
|
||||
$roles = \array_keys($this->hierarchy);
|
||||
|
||||
foreach ($roles as $r) {
|
||||
$childRoles = $this->roleHierarchy->getReachableRoleNames([$r]);
|
||||
|
||||
if (\in_array($role, $childRoles)) {
|
||||
$parentRoles[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
return $parentRoles;
|
||||
trigger_deprecation('Chill\MainBundle', '2.0', 'use ParentRoleHelper::getParentRoles instead');
|
||||
return $this->parentRoleHelper->getParentRoles($role);
|
||||
}
|
||||
}
|
||||
|
73
src/Bundle/ChillMainBundle/Security/ParentRoleHelper.php
Normal file
73
src/Bundle/ChillMainBundle/Security/ParentRoleHelper.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
|
||||
|
||||
/**
|
||||
* Helper which traverse all role to find parents
|
||||
*/
|
||||
class ParentRoleHelper
|
||||
{
|
||||
protected RoleHierarchyInterface $roleHierarchy;
|
||||
|
||||
/**
|
||||
* The role in a hierarchy, given by the parameter
|
||||
* `security.role_hierarchy.roles` from the container.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $hierarchy;
|
||||
|
||||
public function __construct(
|
||||
RoleHierarchyInterface $roleHierarchy,
|
||||
ParameterBagInterface $parameterBag
|
||||
) {
|
||||
$this->roleHierarchy = $roleHierarchy;
|
||||
$this->hierarchy = $parameterBag->get('security.role_hierarchy.roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the role which give access to the given role. Only the role
|
||||
* which are registered into Chill are taken into account.
|
||||
*
|
||||
* The array contains always the current $role (which give access to himself)
|
||||
*
|
||||
* @param string $role
|
||||
* @return string[] the role which give access to the given $role
|
||||
*/
|
||||
public function getParentRoles(string $role): array
|
||||
{
|
||||
$parentRoles = [$role];
|
||||
// transform the roles from role hierarchy from string to Role
|
||||
$roles = \array_keys($this->hierarchy);
|
||||
|
||||
foreach ($roles as $r) {
|
||||
$childRoles = $this->roleHierarchy->getReachableRoleNames([$r]);
|
||||
|
||||
if (\in_array($role, $childRoles)) {
|
||||
$parentRoles[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
return $parentRoles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a parent role may give access to a given child role
|
||||
*
|
||||
* @param string $childRole The role we want to test if he is reachable
|
||||
* @param string $parentRole The role which should give access to $childRole
|
||||
* @return bool true if the child role is granted by parent role
|
||||
*/
|
||||
public function isRoleReached($childRole, $parentRole): bool
|
||||
{
|
||||
$reachableRoles = $this->roleHierarchy
|
||||
->getReachableRoleNames([$parentRole]);
|
||||
|
||||
return in_array($childRole, $reachableRoles);
|
||||
}
|
||||
|
||||
}
|
@@ -98,5 +98,7 @@ class PasswordRecoverVoter extends Voter
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
@@ -12,31 +14,41 @@ class AddressNormalizer implements NormalizerAwareInterface, NormalizerInterface
|
||||
{
|
||||
use NormalizerAwareTrait;
|
||||
|
||||
/**
|
||||
* @param Address $address
|
||||
*/
|
||||
public function normalize($address, string $format = null, array $context = [])
|
||||
{
|
||||
/** @var Address $address */
|
||||
$data['address_id'] = $address->getId();
|
||||
$data['text'] = $address->isNoAddress() ? '' : $address->getStreetNumber().', '.$address->getStreet();
|
||||
$data['street'] = $address->getStreet();
|
||||
$data['streetNumber'] = $address->getStreetNumber();
|
||||
$data['postcode']['id'] = $address->getPostCode()->getId();
|
||||
$data['postcode']['name'] = $address->getPostCode()->getName();
|
||||
$data['postcode']['code'] = $address->getPostCode()->getCode();
|
||||
$data['country']['id'] = $address->getPostCode()->getCountry()->getId();
|
||||
$data['country']['name'] = $address->getPostCode()->getCountry()->getName();
|
||||
$data['country']['code'] = $address->getPostCode()->getCountry()->getCountryCode();
|
||||
$data['floor'] = $address->getFloor();
|
||||
$data['corridor'] = $address->getCorridor();
|
||||
$data['steps'] = $address->getSteps();
|
||||
$data['flat'] = $address->getFlat();
|
||||
$data['buildingName'] = $address->getBuildingName();
|
||||
$data['distribution'] = $address->getDistribution();
|
||||
$data['extra'] = $address->getExtra();
|
||||
$data['validFrom'] = $address->getValidFrom();
|
||||
$data['validTo'] = $address->getValidTo();
|
||||
$data['addressReference'] = $this->normalizer->normalize($address->getAddressReference(), $format, [
|
||||
AbstractNormalizer::GROUPS => ['read']
|
||||
]);
|
||||
$data = [
|
||||
'address_id' => $address->getId(),
|
||||
'text' => $address->isNoAddress() ? '' : $address->getStreetNumber().', '.$address->getStreet(),
|
||||
'street' => $address->getStreet(),
|
||||
'streetNumber' => $address->getStreetNumber(),
|
||||
'postcode' => [
|
||||
'id' => $address->getPostCode()->getId(),
|
||||
'name' => $address->getPostCode()->getName(),
|
||||
'code' => $address->getPostCode()->getCode(),
|
||||
],
|
||||
'country' => [
|
||||
'id' => $address->getPostCode()->getCountry()->getId(),
|
||||
'name' => $address->getPostCode()->getCountry()->getName(),
|
||||
'code' => $address->getPostCode()->getCountry()->getCountryCode(),
|
||||
],
|
||||
'floor' => $address->getFloor(),
|
||||
'corridor' => $address->getCorridor(),
|
||||
'steps' => $address->getSteps(),
|
||||
'flat' => $address->getFlat(),
|
||||
'buildingName' => $address->getBuildingName(),
|
||||
'distribution' => $address->getDistribution(),
|
||||
'extra' => $address->getExtra(),
|
||||
'validFrom' => $address->getValidFrom(),
|
||||
'validTo' => $address->getValidTo(),
|
||||
'addressReference' => $this->normalizer->normalize(
|
||||
$address->getAddressReference(),
|
||||
$format,
|
||||
[AbstractNormalizer::GROUPS => ['read']]
|
||||
),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@@ -9,32 +9,30 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
|
||||
class CollectionNormalizer implements NormalizerInterface, NormalizerAwareInterface
|
||||
{
|
||||
use NormalizerAwareTrait;
|
||||
use NormalizerAwareTrait;
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*/
|
||||
public function normalize($collection, string $format = null, array $context = [])
|
||||
{
|
||||
$paginator = $collection->getPaginator();
|
||||
|
||||
return [
|
||||
'count' => $paginator->getTotalItems(),
|
||||
'pagination' => [
|
||||
'first' => $paginator->getCurrentPageFirstItemNumber(),
|
||||
'items_per_page' => $paginator->getItemsPerPage(),
|
||||
'next' => $paginator->hasNextPage() ? $paginator->getNextPage()->generateUrl() : null,
|
||||
'previous' => $paginator->hasPreviousPage() ? $paginator->getPreviousPage()->generateUrl() : null,
|
||||
'more' => $paginator->hasNextPage(),
|
||||
],
|
||||
'results' => $this->normalizer->normalize($collection->getItems(), $format, $context),
|
||||
];
|
||||
}
|
||||
|
||||
public function supportsNormalization($data, string $format = null): bool
|
||||
{
|
||||
return $data instanceof Collection;
|
||||
}
|
||||
|
||||
public function normalize($collection, string $format = null, array $context = [])
|
||||
{
|
||||
/** @var $collection Collection */
|
||||
$paginator = $collection->getPaginator();
|
||||
|
||||
$data['count'] = $paginator->getTotalItems();
|
||||
$pagination['first'] = $paginator->getCurrentPageFirstItemNumber();
|
||||
$pagination['items_per_page'] = $paginator->getItemsPerPage();
|
||||
$pagination['next'] = $paginator->hasNextPage() ?
|
||||
$paginator->getNextPage()->generateUrl() : null;
|
||||
$pagination['previous'] = $paginator->hasPreviousPage() ?
|
||||
$paginator->getPreviousPage()->generateUrl() : null;
|
||||
$pagination['more'] = $paginator->hasNextPage();
|
||||
$data['pagination'] = $pagination;
|
||||
|
||||
// normalize results
|
||||
$data['results'] = $this->normalizer->normalize($collection->getItems(),
|
||||
$format, $context);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -20,14 +20,21 @@
|
||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* @internal we keep this normalizer, because the property 'text' may be replace by a rendering in the future
|
||||
*/
|
||||
class UserNormalizer implements NormalizerInterface
|
||||
class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface
|
||||
{
|
||||
use NormalizerAwareTrait;
|
||||
private UserRender $userRender;
|
||||
|
||||
public function __construct(UserRender $userRender)
|
||||
{
|
||||
$this->userRender = $userRender;
|
||||
}
|
||||
|
||||
public function normalize($user, string $format = null, array $context = array())
|
||||
{
|
||||
/** @var User $user */
|
||||
@@ -35,7 +42,11 @@ class UserNormalizer implements NormalizerInterface
|
||||
'type' => 'user',
|
||||
'id' => $user->getId(),
|
||||
'username' => $user->getUsername(),
|
||||
'text' => $user->getUsername()
|
||||
'text' => $this->userRender->renderString($user, []),
|
||||
'label' => $user->getLabel(),
|
||||
'user_job' => $this->normalizer->normalize($user->getUserJob(), $format, $context),
|
||||
'main_center' => $this->normalizer->normalize($user->getMainCenter(), $format, $context),
|
||||
'main_scope' => $this->normalizer->normalize($user->getMainScope(), $format, $context),
|
||||
];
|
||||
}
|
||||
|
||||
|
66
src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php
Normal file
66
src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Templating\Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
|
||||
class UserRender implements ChillEntityRenderInterface
|
||||
{
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
private EngineInterface $engine;
|
||||
|
||||
const DEFAULT_OPTIONS = [
|
||||
'main_scope' => true,
|
||||
'user_job' => true
|
||||
];
|
||||
|
||||
public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine)
|
||||
{
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supports($entity, array $options): bool
|
||||
{
|
||||
return $entity instanceof User;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param User $entity
|
||||
*/
|
||||
public function renderString($entity, array $options): string
|
||||
{
|
||||
$opts = \array_merge(self::DEFAULT_OPTIONS, $options);
|
||||
|
||||
$str = $entity->getLabel();
|
||||
if (NULL !== $entity->getUserJob() && $opts['user_job']) {
|
||||
$str .= ' ('.$this->translatableStringHelper
|
||||
->localize($entity->getUserJob()->getLabel()).')';
|
||||
}
|
||||
if (NULL !== $entity->getMainScope() && $opts['main_scope']) {
|
||||
$str .= ' ('.$this->translatableStringHelper
|
||||
->localize($entity->getMainScope()->getName()).')';
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function renderBox($entity, array $options): string
|
||||
{
|
||||
$opts = \array_merge(self::DEFAULT_OPTIONS, $options);
|
||||
|
||||
return $this->engine->render('@ChillMain/Entity/user.html.twig', [
|
||||
'user' => $entity,
|
||||
'opts' => $opts
|
||||
]);
|
||||
}
|
||||
}
|
@@ -12,6 +12,12 @@ class FilterOrderHelper
|
||||
private FormFactoryInterface $formFactory;
|
||||
private RequestStack $requestStack;
|
||||
private ?array $searchBoxFields = null;
|
||||
private array $checkboxes = [];
|
||||
private ?array $submitted = null;
|
||||
private ?string $formName = 'f';
|
||||
private string $formType = FilterOrderType::class;
|
||||
private array $formOptions = [];
|
||||
|
||||
|
||||
public function __construct(
|
||||
FormFactoryInterface $formFactory,
|
||||
@@ -28,6 +34,32 @@ class FilterOrderHelper
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = []): self
|
||||
{
|
||||
$missing = count($choices) - count($trans) - 1;
|
||||
$this->checkboxes[$name] = [
|
||||
'choices' => $choices, 'default' => $default,
|
||||
'trans' =>
|
||||
\array_merge(
|
||||
$trans,
|
||||
0 < $missing ?
|
||||
array_fill(0, $missing, null) : []
|
||||
)
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCheckboxData(string $name): array
|
||||
{
|
||||
return $this->getFormData()['checkboxes'][$name];
|
||||
}
|
||||
|
||||
public function getCheckboxes(): array
|
||||
{
|
||||
return $this->checkboxes;
|
||||
}
|
||||
|
||||
public function hasSearchBox(): bool
|
||||
{
|
||||
return $this->searchBoxFields !== null;
|
||||
@@ -35,26 +67,41 @@ class FilterOrderHelper
|
||||
|
||||
private function getFormData(): array
|
||||
{
|
||||
return [
|
||||
'q' => $this->getQueryString()
|
||||
];
|
||||
if (NULL === $this->submitted) {
|
||||
$this->submitted = $this->buildForm()
|
||||
->getData();
|
||||
}
|
||||
|
||||
return $this->submitted;
|
||||
}
|
||||
|
||||
private function getDefaultData(): array
|
||||
{
|
||||
$r = [];
|
||||
|
||||
if ($this->hasSearchBox()) {
|
||||
$r['q'] = '';
|
||||
}
|
||||
foreach ($this->checkboxes as $name => $c) {
|
||||
$r['checkboxes'][$name] = $c['default'];
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function getQueryString(): ?string
|
||||
{
|
||||
$q = $this->requestStack->getCurrentRequest()
|
||||
->query->get('q', null);
|
||||
|
||||
return empty($q) ? NULL : $q;
|
||||
return $this->getFormData()['q'];
|
||||
}
|
||||
|
||||
public function buildForm($name = null, string $type = FilterOrderType::class, array $options = []): FormInterface
|
||||
public function buildForm(): FormInterface
|
||||
{
|
||||
return $this->formFactory
|
||||
->createNamed($name, $type, $this->getFormData(), \array_merge([
|
||||
->createNamed($this->formName, $this->formType, $this->getDefaultData(), \array_merge([
|
||||
'helper' => $this,
|
||||
'method' => 'GET',
|
||||
'csrf_protection' => false,
|
||||
], $options));
|
||||
], $this->formOptions))
|
||||
->handleRequest($this->requestStack->getCurrentRequest());
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
||||
class FilterOrderHelperBuilder
|
||||
{
|
||||
private ?array $searchBoxFields = null;
|
||||
private array $checkboxes = [];
|
||||
private FormFactoryInterface $formFactory;
|
||||
private RequestStack $requestStack;
|
||||
|
||||
@@ -19,13 +20,20 @@ class FilterOrderHelperBuilder
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
public function addSearchBox(array $fields, ?array $options = []): self
|
||||
public function addSearchBox(?array $fields = [], ?array $options = []): self
|
||||
{
|
||||
$this->searchBoxFields = $fields;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = []): self
|
||||
{
|
||||
$this->checkboxes[$name] = [ 'choices' => $choices, 'default' => $default, 'trans' => $trans];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function build(): FilterOrderHelper
|
||||
{
|
||||
$helper = new FilterOrderHelper(
|
||||
@@ -34,6 +42,13 @@ class FilterOrderHelperBuilder
|
||||
);
|
||||
|
||||
$helper->setSearchBox($this->searchBoxFields);
|
||||
foreach ($this->checkboxes as $name => [
|
||||
'choices' => $choices,
|
||||
'default' => $default,
|
||||
'trans' => $trans
|
||||
]) {
|
||||
$helper->addCheckbox($name, $choices, $default, $trans);
|
||||
}
|
||||
|
||||
return $helper;
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Tests\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Security\ParentRoleHelper;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
class ParentRoleHelperTest extends KernelTestCase
|
||||
{
|
||||
private ParentRoleHelper $parentRoleHelper;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->parentRoleHelper = self::$container->get(ParentRoleHelper::class);
|
||||
}
|
||||
|
||||
public function testGetReachableRoles()
|
||||
{
|
||||
// this test will be valid until the role hierarchy for person is changed.
|
||||
// this is not perfect but spare us a mock
|
||||
|
||||
$parentRoles = $this->parentRoleHelper->getParentRoles(PersonVoter::SEE);
|
||||
|
||||
$this->assertCount(3, $parentRoles);
|
||||
$this->assertContains(PersonVoter::CREATE, $parentRoles);
|
||||
$this->assertContains(PersonVoter::UPDATE, $parentRoles);
|
||||
$this->assertContains(PersonVoter::SEE, $parentRoles);
|
||||
}
|
||||
|
||||
public function testIsRoleReached()
|
||||
{
|
||||
$this->assertTrue($this->parentRoleHelper->isRoleReached(PersonVoter::SEE, PersonVoter::CREATE));
|
||||
$this->assertFalse($this->parentRoleHelper->isRoleReached(PersonVoter::SEE, 'foo'));
|
||||
}
|
||||
}
|
@@ -37,6 +37,18 @@ class DateRangeCoveringTest extends TestCase
|
||||
$this->assertNotContains(3, $cover->getIntersections()[0][2]);
|
||||
}
|
||||
|
||||
public function testCoveringWithMinCover1_NoCoveringWithNullDates()
|
||||
{
|
||||
$cover = new DateRangeCovering(1, new \DateTimeZone('Europe/Brussels'));
|
||||
$cover
|
||||
->add(new \DateTime('2021-10-05'), new \DateTime('2021-10-18'), 521)
|
||||
->add(new \DateTime('2021-10-26'), null, 663)
|
||||
->compute()
|
||||
;
|
||||
|
||||
$this->assertFalse($cover->hasIntersections());
|
||||
}
|
||||
|
||||
public function testCoveringWithMinCover1WithTwoIntersections()
|
||||
{
|
||||
$cover = new DateRangeCovering(1, new \DateTimeZone('Europe/Brussels'));
|
||||
|
@@ -20,7 +20,7 @@
|
||||
namespace Chill\MainBundle\Timeline;
|
||||
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
@@ -31,38 +31,38 @@ use Doctrine\ORM\NativeQuery;
|
||||
*/
|
||||
class TimelineBuilder implements ContainerAwareInterface
|
||||
{
|
||||
|
||||
|
||||
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Doctrine\ORM\EntityManagerInterface
|
||||
*/
|
||||
private $em;
|
||||
|
||||
|
||||
/**
|
||||
* Record provider
|
||||
*
|
||||
*
|
||||
* This array has the structure `[ 'service id' => $service ]`
|
||||
*
|
||||
* @var TimelineProviderInterface[]
|
||||
*/
|
||||
private $providers = [];
|
||||
|
||||
|
||||
/**
|
||||
* Record provider and their context
|
||||
*
|
||||
*
|
||||
* This array has the structure `[ 'context' => [ 'service id' ] ]`
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $providersByContext = [];
|
||||
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return an HTML string with timeline
|
||||
*
|
||||
@@ -79,18 +79,18 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
public function getTimelineHTML($context, array $args, $firstItem = 0, $number = 20)
|
||||
{
|
||||
list($union, $parameters) = $this->buildUnionQuery($context, $args);
|
||||
|
||||
|
||||
//add ORDER BY clause and LIMIT
|
||||
$query = $union . sprintf(' ORDER BY date DESC LIMIT %d OFFSET %d',
|
||||
$number, $firstItem);
|
||||
|
||||
|
||||
// run query and handle results
|
||||
$fetched = $this->runUnionQuery($query, $parameters);
|
||||
$entitiesByKey = $this->getEntities($fetched, $context);
|
||||
|
||||
|
||||
return $this->render($fetched, $entitiesByKey, $context, $args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of items for the given context and args
|
||||
*
|
||||
@@ -101,7 +101,7 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
public function countItems($context, array $args)
|
||||
{
|
||||
$rsm = (new ResultSetMapping())
|
||||
->addScalarResult('total', 'total', Type::INTEGER);
|
||||
->addScalarResult('total', 'total', Types::INTEGER);
|
||||
|
||||
list($select, $parameters) = $this->buildUnionQuery($context, $args);
|
||||
|
||||
@@ -110,10 +110,10 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
|
||||
$nq = $this->em->createNativeQuery($countQuery, $rsm);
|
||||
$nq->setParameters($parameters);
|
||||
|
||||
|
||||
return $nq->getSingleScalarResult();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* add a provider id
|
||||
*
|
||||
@@ -127,7 +127,7 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
$this->providersByContext[$context][] = $id;
|
||||
$this->providers[$id] = $provider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get providers by context
|
||||
*
|
||||
@@ -141,16 +141,16 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
throw new \LogicException(sprintf('No builders have been defined for "%s"'
|
||||
. ' context', $context));
|
||||
}
|
||||
|
||||
|
||||
$providers = [];
|
||||
|
||||
|
||||
foreach($this->providersByContext[$context] as $providerId) {
|
||||
$providers[] = $this->providers[$providerId];
|
||||
}
|
||||
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* build the UNION query with all providers
|
||||
*
|
||||
@@ -172,7 +172,7 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
$union .= $append;
|
||||
$parameters = array_merge($parameters, $selectParameters);
|
||||
}
|
||||
|
||||
|
||||
return [$union, $parameters];
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
return $s;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* return the SQL SELECT query as a string,
|
||||
*
|
||||
@@ -222,9 +222,9 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
);
|
||||
|
||||
return [$sql, $data['WHERE'][1]];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* run the UNION query and return result as an array
|
||||
*
|
||||
@@ -236,12 +236,12 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
->addScalarResult('id', 'id')
|
||||
->addScalarResult('type', 'type')
|
||||
->addScalarResult('date', 'date');
|
||||
|
||||
|
||||
return $this->em->createNativeQuery($query, $resultSetMapping)
|
||||
->setParameters($parameters)
|
||||
->getArrayResult();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $queriedIds
|
||||
@@ -252,11 +252,11 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
{
|
||||
//gather entities by type to pass all id with same type to the TimelineProvider.
|
||||
$idsByType = array();
|
||||
|
||||
|
||||
foreach($queriedIds as $result) {
|
||||
$idsByType[$result['type']][] = $result['id'];
|
||||
}
|
||||
|
||||
|
||||
//fetch entities from providers
|
||||
$entitiesByType = array();
|
||||
foreach ($idsByType as $type => $ids) {
|
||||
@@ -268,10 +268,10 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $entitiesByType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* render the timeline as HTML
|
||||
*
|
||||
@@ -291,20 +291,21 @@ class TimelineBuilder implements ContainerAwareInterface
|
||||
$entitiesByType[$result['type']][$result['id']], //the entity
|
||||
$context,
|
||||
$args);
|
||||
$timelineEntry['date'] = new \DateTime($result['date']);
|
||||
$timelineEntry['template'] = $data['template'];
|
||||
$timelineEntry['template_data'] = $data['template_data'];
|
||||
|
||||
$timelineEntries[] = $timelineEntry;
|
||||
|
||||
$timelineEntries[] = [
|
||||
'date' => new \DateTime($result['date']),
|
||||
'template' => $data['template'],
|
||||
'template_data' => $data['template_data']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
return $this->container->get('templating')
|
||||
->render('@ChillMain/Timeline/chain_timelines.html.twig', array(
|
||||
'results' => $timelineEntries
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the template data from the provider for the given entity, by type.
|
||||
*
|
||||
|
@@ -5,15 +5,15 @@ namespace Chill\MainBundle\Util;
|
||||
/**
|
||||
* Utilities to compare date periods
|
||||
*
|
||||
* This class allow to compare periods when there are period covering. The
|
||||
* This class allow to compare periods when there are period covering. The
|
||||
* argument `minCovers` allow to find also when there are more than 2 period
|
||||
* which intersects.
|
||||
* which intersects.
|
||||
*
|
||||
* Example: a team may have maximum 2 leaders on a same period: you will
|
||||
* Example: a team may have maximum 2 leaders on a same period: you will
|
||||
* find here all periods where there are more than 2 leaders.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
*
|
||||
* ```php
|
||||
* $cover = new DateRangeCovering(2); // 2 means we will have periods
|
||||
* // when there are 2+ periods intersecting
|
||||
@@ -73,7 +73,7 @@ class DateRangeCovering
|
||||
$this->addToSequence($start->getTimestamp(), $k, null);
|
||||
$this->addToSequence(
|
||||
NULL === $end ? PHP_INT_MAX : $end->getTimestamp(), null, $k
|
||||
);
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -140,72 +140,11 @@ class DateRangeCovering
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function process(array $intersections): array
|
||||
{
|
||||
$result = [];
|
||||
$starts = [];
|
||||
$ends = [];
|
||||
$metadatas = [];
|
||||
|
||||
while (null !== ($current = \array_pop($intersections))) {
|
||||
list($cStart, $cEnd, $cMetadata) = $current;
|
||||
$n = count($cMetadata);
|
||||
|
||||
foreach ($intersections as list($iStart, $iEnd, $iMetadata)) {
|
||||
$start = max($cStart, $iStart);
|
||||
$end = min($cEnd, $iEnd);
|
||||
|
||||
if ($start <= $end) {
|
||||
if (FALSE !== ($key = \array_search($start, $starts))) {
|
||||
if ($ends[$key] === $end) {
|
||||
$metadatas[$key] = \array_unique(\array_merge($metadatas[$key], $iMetadata));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$starts[] = $start;
|
||||
$ends[] = $end;
|
||||
$metadatas[] = \array_unique(\array_merge($iMetadata, $cMetadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recompose results
|
||||
foreach ($starts as $k => $start) {
|
||||
$result[] = [$start, $ends[$k], \array_unique($metadatas[$k])];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function addToIntersections(array $intersections, array $intersection)
|
||||
{
|
||||
$foundExisting = false;
|
||||
list($nStart, $nEnd, $nMetadata) = $intersection;
|
||||
|
||||
\array_walk($intersections,
|
||||
function(&$i, $key) use ($nStart, $nEnd, $nMetadata, $foundExisting) {
|
||||
if ($foundExisting) {
|
||||
return;
|
||||
};
|
||||
if ($i[0] === $nStart && $i[1] === $nEnd) {
|
||||
$foundExisting = true;
|
||||
$i[2] = \array_merge($i[2], $nMetadata);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!$foundExisting) {
|
||||
$intersections[] = $intersection;
|
||||
}
|
||||
|
||||
return $intersections;
|
||||
}
|
||||
|
||||
public function hasIntersections(): bool
|
||||
{
|
||||
if (!$this->computed) {
|
||||
throw new \LogicException(sprintf("You cannot call the method %s before ".
|
||||
"'process'", __METHOD));
|
||||
"'process'", __METHOD__));
|
||||
}
|
||||
|
||||
return count($this->intersections) > 0;
|
||||
@@ -215,7 +154,7 @@ class DateRangeCovering
|
||||
{
|
||||
if (!$this->computed) {
|
||||
throw new \LogicException(sprintf("You cannot call the method %s before ".
|
||||
"'process'", __METHOD));
|
||||
"'process'", __METHOD__));
|
||||
}
|
||||
|
||||
return $this->intersections;
|
||||
|
@@ -51,6 +51,7 @@ module.exports = function(encore, entries)
|
||||
|
||||
// Page entrypoints
|
||||
encore.addEntry('page_login', __dirname + '/Resources/public/page/login/index.js');
|
||||
encore.addEntry('page_location', __dirname + '/Resources/public/page/location/index.js');
|
||||
|
||||
buildCKEditor(encore);
|
||||
|
||||
@@ -59,6 +60,7 @@ module.exports = function(encore, entries)
|
||||
encore.addEntry('mod_bootstrap', __dirname + '/Resources/public/module/bootstrap/index.js');
|
||||
encore.addEntry('mod_ckeditor5', __dirname + '/Resources/public/module/ckeditor5/index.js');
|
||||
encore.addEntry('mod_disablebuttons', __dirname + '/Resources/public/module/disable-buttons/index.js');
|
||||
encore.addEntry('mod_blur', __dirname + '/Resources/public/module/blur/index.js');
|
||||
|
||||
encore.addEntry('mod_input_address', __dirname + '/Resources/public/vuejs/Address/mod_input_address_index.js');
|
||||
// Vue entrypoints
|
||||
|
@@ -66,6 +66,10 @@ chill_main_admin_permissions:
|
||||
path: /{_locale}/admin/permissions
|
||||
controller: Chill\MainBundle\Controller\AdminController::indexPermissionsAction
|
||||
|
||||
chill_main_admin_locations:
|
||||
path: /{_locale}/admin/locations
|
||||
controller: Chill\MainBundle\Controller\AdminController::indexLocationsAction
|
||||
|
||||
chill_main_search:
|
||||
path: /{_locale}/search.{_format}
|
||||
controller: Chill\MainBundle\Controller\SearchController::searchAction
|
||||
|
@@ -11,6 +11,8 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository'
|
||||
|
||||
Chill\MainBundle\Serializer\Normalizer\:
|
||||
resource: '../Serializer/Normalizer'
|
||||
autoconfigure: true
|
||||
|
@@ -25,6 +25,7 @@ services:
|
||||
- "@request_stack"
|
||||
- "@doctrine.orm.entity_manager"
|
||||
- "@chill.main.helper.translatable_string"
|
||||
- '@Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface'
|
||||
tags:
|
||||
- { name: form.type, alias: select2_chill_country }
|
||||
|
||||
@@ -34,6 +35,7 @@ services:
|
||||
- "@request_stack"
|
||||
- "@doctrine.orm.entity_manager"
|
||||
- "@chill.main.helper.translatable_string"
|
||||
- '@Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface'
|
||||
tags:
|
||||
- { name: form.type, alias: select2_chill_language }
|
||||
|
||||
@@ -137,3 +139,7 @@ services:
|
||||
Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
|
||||
Chill\MainBundle\Form\Type\LocationFormType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
@@ -40,6 +40,10 @@ services:
|
||||
Chill\MainBundle\Security\Authorization\AuthorizationHelper: '@chill.main.security.authorization.helper'
|
||||
Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface: '@chill.main.security.authorization.helper'
|
||||
|
||||
Chill\MainBundle\Security\ParentRoleHelper:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill.main.role_provider:
|
||||
class: Chill\MainBundle\Security\RoleProvider
|
||||
Chill\MainBundle\Security\RoleProvider: '@chill.main.role_provider'
|
||||
|
@@ -42,10 +42,12 @@ services:
|
||||
- { name: twig.extension }
|
||||
|
||||
Chill\MainBundle\Templating\Entity\AddressRender:
|
||||
arguments:
|
||||
- '@Symfony\Component\Templating\EngineInterface'
|
||||
tags:
|
||||
- { name: 'chill.render_entity' }
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
|
||||
Chill\MainBundle\Templating\Entity\UserRender:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
|
||||
Chill\MainBundle\Templating\Listing\:
|
||||
resource: './../../Templating/Listing'
|
||||
|
@@ -20,7 +20,6 @@ final class Version20210304085819 extends AbstractMigration
|
||||
public function up(Schema $schema) : void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users ADD attributes JSONB DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN users.attributes IS \'(DC2Type:json_array)\'');
|
||||
}
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add active on Location and LocationType
|
||||
*/
|
||||
final class Version20211022094429 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add active on Location and LocationType';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_main_location ADD active BOOLEAN DEFAULT TRUE;');
|
||||
$this->addSql('ALTER TABLE chill_main_location_type ADD active BOOLEAN DEFAULT TRUE;');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_main_location_type DROP active');
|
||||
$this->addSql('ALTER TABLE chill_main_location DROP active');
|
||||
}
|
||||
}
|
@@ -74,7 +74,7 @@ Choose a postal code: Choisir un code postal
|
||||
address:
|
||||
address_homeless: L'adresse est-elle celle d'un domicile fixe ?
|
||||
real address: Adresse d'un domicile
|
||||
consider homeless: N'est pas l'adresse d'un domicile (SDF)
|
||||
consider homeless: Cette adresse est incomplète
|
||||
address more:
|
||||
floor: ét
|
||||
corridor: coul
|
||||
@@ -110,11 +110,16 @@ edit: modifier
|
||||
Main admin menu: Menu d'administration principal
|
||||
Actions: Actions
|
||||
Users and permissions: Utilisateurs et permissions
|
||||
Location and location type: Localisations et types de localisation
|
||||
|
||||
#permissions
|
||||
Permissions Menu: Gestion des droits
|
||||
Permissions management of your chill installation: Gestion des permissions de votre instance
|
||||
|
||||
#location
|
||||
Location Menu: Localisations et types de localisation
|
||||
Management of location: Gestion des localisations et types de localisation
|
||||
|
||||
#admin section
|
||||
"Administration interface": Interface d'administration
|
||||
Welcome to the admin section !: >
|
||||
@@ -180,6 +185,24 @@ Circle edit: Modification du cercle
|
||||
Circle creation: Création d'un cercle
|
||||
Create a new circle: Créer un nouveau cercle
|
||||
|
||||
#admin section for location
|
||||
Location: Localisation
|
||||
Location type list: Liste des types de localisation
|
||||
Create a new location type: Créer un nouveau type de localisation
|
||||
Available for users: Disponible aux utilisateurs
|
||||
Address required: Adresse requise?
|
||||
Contact data: Données de contact?
|
||||
optional: optionnel
|
||||
required: requis
|
||||
never: jamais
|
||||
Create a new location: Créer une nouvelle localisation
|
||||
Location list: Liste des localisations
|
||||
Location type: Type de localisation
|
||||
Phonenumber1: Numéro de téléphone
|
||||
Phonenumber2: Autre numéro de téléphone
|
||||
Configure location: Configuration des localisations
|
||||
Configure location type: Configuration des types de localisations
|
||||
|
||||
# circles / scopes
|
||||
Choose the circle: Choisir le cercle
|
||||
|
||||
@@ -294,6 +317,12 @@ crud:
|
||||
add_new: Créer
|
||||
title_new: Nouveau métier
|
||||
title_edit: Modifier un métier
|
||||
main_location_type:
|
||||
title_new: Nouveau type de localisation
|
||||
title_edit: Modifier un type de localisation
|
||||
main_location:
|
||||
title_new: Nouvelle localisation
|
||||
title_edit: Modifier une localisation
|
||||
|
||||
No entities: Aucun élément
|
||||
|
||||
|
Reference in New Issue
Block a user