Merge remote-tracking branch 'origin/master' into issue133_user_current_location

This commit is contained in:
2021-11-22 13:43:22 +01:00
176 changed files with 4626 additions and 3148 deletions

View File

@@ -4,15 +4,23 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Repository\LocationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@@ -25,26 +33,54 @@ use Symfony\Component\Serializer\SerializerInterface;
final class ActivityController extends AbstractController
{
protected EventDispatcherInterface $eventDispatcher;
private EventDispatcherInterface $eventDispatcher;
protected AuthorizationHelper $authorizationHelper;
private LoggerInterface $logger;
protected LoggerInterface $logger;
private SerializerInterface $serializer;
protected SerializerInterface $serializer;
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
protected ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
private ActivityTypeRepository $activityTypeRepository;
private ThirdPartyRepository $thirdPartyRepository;
private PersonRepository $personRepository;
private LocationRepository $locationRepository;
private EntityManagerInterface $entityManager;
private ActivityRepository $activityRepository;
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
LocationRepository $locationRepository,
ActivityRepository $activityRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper,
LoggerInterface $logger,
SerializerInterface $serializer
) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->activityTypeRepository = $activityTypeRepository;
$this->activityTypeCategoryRepository = $activityTypeCategoryRepository;
$this->personRepository = $personRepository;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->locationRepository = $locationRepository;
$this->activityRepository = $activityRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->entityManager = $entityManager;
$this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper;
$this->logger = $logger;
$this->serializer = $serializer;
}
@@ -65,10 +101,10 @@ final class ActivityController extends AbstractController
$activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null);
$event = new PrivacyEvent($person, array(
$event = new PrivacyEvent($person, [
'element_class' => Activity::class,
'action' => 'list'
));
]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$view = 'ChillActivityBundle:Activity:listPerson.html.twig';
@@ -93,7 +129,6 @@ final class ActivityController extends AbstractController
public function selectTypeAction(Request $request): Response
{
$em = $this->getDoctrine()->getManager();
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -106,12 +141,17 @@ final class ActivityController extends AbstractController
$data = [];
$activityTypeCategories = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityTypeCategory::class)
$activityTypeCategories = $this
->activityTypeCategoryRepository
->findBy(['active' => true], ['ordering' => 'ASC']);
foreach ($activityTypeCategories as $activityTypeCategory) {
$activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']);
$activityTypes = $this
->activityTypeRepository
->findBy(
['active' => true, 'category' => $activityTypeCategory],
['ordering' => 'ASC']
);
$data[] = [
'activityTypeCategory' => $activityTypeCategory,
@@ -119,12 +159,6 @@ final class ActivityController extends AbstractController
];
}
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
} else {
$activityData = [];
}
if ($view === null) {
throw $this->createNotFoundException('Template not found');
}
@@ -133,14 +167,13 @@ final class ActivityController extends AbstractController
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'data' => $data,
'activityData' => $activityData
'activityData' => $request->query->get('activityData', []),
]);
}
public function newAction(Request $request): Response
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -151,8 +184,7 @@ final class ActivityController extends AbstractController
}
$activityType_id = $request->get('activityType_id', 0);
$activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
->find($activityType_id);
$activityType = $this->activityTypeRepository->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) {
throw new \InvalidArgumentException('Activity type must be active');
@@ -210,20 +242,20 @@ final class ActivityController extends AbstractController
if (array_key_exists('personsId', $activityData)) {
foreach($activityData['personsId'] as $personId){
$concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId);
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData)) {
foreach($activityData['professionalsId'] as $professionalsId){
$professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId);
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (array_key_exists('location', $activityData)) {
$location = $em->getRepository(\Chill\MainBundle\Entity\Location::class)->find($activityData['location']);
$location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location);
}
@@ -248,8 +280,8 @@ final class ActivityController extends AbstractController
])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity);
$em->flush();
$this->entityManager->persist($entity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
@@ -281,7 +313,6 @@ final class ActivityController extends AbstractController
public function showAction(Request $request, int $id): Response
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -291,13 +322,14 @@ final class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:showPerson.html.twig';
}
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
$entity = $this->activityRepository->find($id);
if (!$entity) {
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
}
if (null !== $accompanyingPeriod) {
// @TODO: Properties created dynamically.
$entity->personsAssociated = $entity->getPersonsAssociated();
$entity->personsNotAssociated = $entity->getPersonsNotAssociated();
}
@@ -305,7 +337,7 @@ final class ActivityController extends AbstractController
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod);
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
// TODO
/*
@@ -321,22 +353,20 @@ final class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, array(
return $this->render($view, [
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
));
]);
}
/**
* Displays a form to edit an existing Activity entity.
*
*/
public function editAction(int $id, Request $request): Response
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -346,9 +376,9 @@ final class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
}
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
$entity = $this->activityRepository->find($id);
if (!$entity) {
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
}
@@ -363,17 +393,18 @@ final class ActivityController extends AbstractController
])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity);
$em->flush();
$this->entityManager->persist($entity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id;
$params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod);
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
/*
* TODO
@@ -391,24 +422,22 @@ final class ActivityController extends AbstractController
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, array(
return $this->render($view, [
'entity' => $entity,
'edit_form' => $form->createView(),
'delete_form' => $deleteForm->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'activity_json' => $activity_array
));
]);
}
/**
* Deletes a Activity entity.
*
*/
public function deleteAction(Request $request, $id)
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -418,8 +447,7 @@ final class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
}
/* @var $activity Activity */
$activity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
$activity = $this->activityRepository->find($id);
if (!$activity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
@@ -428,35 +456,37 @@ final class ActivityController extends AbstractController
// TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
$form = $this->createDeleteForm($id, $person, $accompanyingPeriod);
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
if ($form->isValid()) {
$this->logger->notice("An activity has been removed", array(
$this->logger->notice("An activity has been removed", [
'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(),
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
'comment' => $activity->getComment()->getComment(),
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
'reasons_ids' => $activity->getReasons()
->map(function ($ar) { return $ar->getId(); })
->map(
static fn (ActivityReason $ar): int => $ar->getId()
)
->toArray(),
'type_id' => $activity->getType()->getId(),
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
'date' => $activity->getDate()->format('Y-m-d'),
'attendee' => $activity->getAttendee()
));
]);
$em->remove($activity);
$em->flush();
$this->entityManager->remove($activity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')
->trans("The activity has been successfully removed."));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
return $this->redirectToRoute('chill_activity_activity_list', $params);
}
}
@@ -465,18 +495,18 @@ final class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, array(
return $this->render($view, [
'activity' => $activity,
'delete_form' => $form->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
));
]);
}
/**
* Creates a form to delete a Activity entity by id.
*/
private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): Form
private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
{
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id;
@@ -484,19 +514,17 @@ final class ActivityController extends AbstractController
return $this->createFormBuilder()
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete'))
->getForm()
;
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}
private function getEntity(Request $request): array
{
$em = $this->getDoctrine()->getManager();
$person = $accompanyingPeriod = null;
if ($request->query->has('person_id')) {
$person_id = $request->get('person_id');
$person = $em->getRepository(Person::class)->find($person_id);
$person = $this->personRepository->find($person_id);
if ($person === null) {
throw $this->createNotFoundException('Person not found');
@@ -505,7 +533,7 @@ final class ActivityController extends AbstractController
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} elseif ($request->query->has('accompanying_period_id')) {
$accompanying_period_id = $request->get('accompanying_period_id');
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id);
$accompanyingPeriod = $this->accompanyingPeriodRepository->find($accompanying_period_id);
if ($accompanyingPeriod === null) {
throw $this->createNotFoundException('Accompanying Period not found');
@@ -518,21 +546,20 @@ final class ActivityController extends AbstractController
}
return [
$person, $accompanyingPeriod
$person,
$accompanyingPeriod
];
}
private function buildParamsToUrl(
?Person $person,
?AccompanyingPeriod $accompanyingPeriod
): array {
private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array
{
$params = [];
if ($person) {
if (null !== $person) {
$params['person_id'] = $person->getId();
}
if ($accompanyingPeriod) {
if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
}

View File

@@ -36,6 +36,8 @@ use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* @var \Faker\Generator
*/
@@ -80,15 +82,10 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
*
* @return \Chill\ActivityBundle\Entity\ActivityReason
*/
private function getRandomActivityReason(array $excludingIds)
private function getRandomActivityReason()
{
$reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)];
if (in_array($this->getReference($reasonRef)->getId(), $excludingIds, true)) {
// we have a reason which should be excluded. Find another...
return $this->getRandomActivityReason($excludingIds);
}
return $this->getReference($reasonRef);
}
@@ -103,7 +100,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
return $this->getReference($userRef);
}
public function newRandomActivity($person)
public function newRandomActivity($person): ?Activity
{
$activity = (new Activity())
->setUser($this->getRandomUser())
@@ -116,11 +113,13 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
// ->setAttendee($this->faker->boolean())
$usedId = array();
for ($i = 0; $i < rand(0, 4); $i++) {
$reason = $this->getRandomActivityReason($usedId);
$usedId[] = $reason->getId();
$activity->addReason($reason);
$reason = $this->getRandomActivityReason();
if (null !== $reason) {
$activity->addReason($reason);
} else {
return null;
}
}
return $activity;
@@ -137,7 +136,9 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
for ($i = 0; $i < $activityNbr; $i ++) {
$activity = $this->newRandomActivity($person);
$manager->persist($activity);
if (null !== $activity) {
$manager->persist($activity);
}
}
}
$manager->flush();

View File

@@ -1,27 +1,8 @@
<?php
/*
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Entity;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Location;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
@@ -38,7 +19,7 @@ use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
@@ -202,7 +183,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer
return $this->id;
}
public function setUser(User $user): self
public function setUser(UserInterface $user): self
{
$this->user = $user;

View File

@@ -1,31 +1,12 @@
<?php
/*
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class ActivityTypeCateogry
*
* @package Chill\ActivityBundle\Entity
* @ORM\Entity()
* @ORM\Table(name="activitytypecategory")
* @ORM\HasLifecycleCallbacks()
@@ -37,7 +18,7 @@ class ActivityTypeCategory
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="json")
@@ -54,10 +35,7 @@ class ActivityTypeCategory
*/
private float $ordering = 0.0;
/**
* Get id
*/
public function getId(): int
public function getId(): ?int
{
return $this->id;
}

View File

@@ -1,70 +1,39 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonAggregator implements AggregatorInterface,
ExportElementValidatedInterface
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
/**
*
* @var EntityRepository
*/
protected $categoryRepository;
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
/**
*
* @var EntityRepository
*/
protected $reasonRepository;
protected ActivityReasonRepository $activityReasonRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
EntityRepository $categoryRepository,
EntityRepository $reasonRepository,
TranslatableStringHelper $stringHelper
ActivityReasonCategoryRepository $activityReasonCategoryRepository,
ActivityReasonRepository $activityReasonRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->categoryRepository = $categoryRepository;
$this->reasonRepository = $reasonRepository;
$this->stringHelper = $stringHelper;
$this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
$this->activityReasonRepository = $activityReasonRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function alterQuery(QueryBuilder $qb, $data)
@@ -77,7 +46,7 @@ class ActivityReasonAggregator implements AggregatorInterface,
$elem = 'category.id';
$alias = 'activity_categories_id';
} else {
throw new \RuntimeException('the data provided are not recognized');
throw new \RuntimeException('The data provided are not recognized.');
}
$qb->addSelect($elem.' as '.$alias);
@@ -93,11 +62,12 @@ class ActivityReasonAggregator implements AggregatorInterface,
(! array_key_exists('activity', $join))
) {
$qb->add(
'join',
array('activity' =>
new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
),
true);
'join',
[
'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
],
true
);
}
// join category if necessary
@@ -143,28 +113,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('level', ChoiceType::class, array(
'choices' => array(
'By reason' => 'reasons',
'By category of reason' => 'categories'
),
'multiple' => false,
'expanded' => true,
'label' => 'Reason\'s level'
));
$builder->add(
'level',
ChoiceType::class,
[
'choices' => [
'By reason' => 'reasons',
'By category of reason' => 'categories'
],
'multiple' => false,
'expanded' => true,
'label' => "Reason's level"
]
);
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['level'] === null) {
$context->buildViolation("The reasons's level should not be empty")
$context
->buildViolation("The reasons's level should not be empty.")
->addViolation();
}
}
public function getTitle()
public function getTitle()
{
return "Aggregate by activity reason";
return 'Aggregate by activity reason';
}
public function addRole()
@@ -177,41 +152,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
// for performance reason, we load data from db only once
switch ($data['level']) {
case 'reasons':
$this->reasonRepository->findBy(array('id' => $values));
$this->activityReasonRepository->findBy(['id' => $values]);
break;
case 'categories':
$this->categoryRepository->findBy(array('id' => $values));
$this->activityReasonCategoryRepository->findBy(['id' => $values]);
break;
default:
throw new \RuntimeException(sprintf("the level data '%s' is invalid",
$data['level']));
throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
}
return function($value) use ($data) {
if ($value === '_header') {
return $data['level'] === 'reasons' ?
'Group by reasons'
:
'Group by categories of reason'
;
return $data['level'] === 'reasons' ? 'Group by reasons' : 'Group by categories of reason';
}
switch ($data['level']) {
case 'reasons':
/* @var $r \Chill\ActivityBundle\Entity\ActivityReason */
$r = $this->reasonRepository->find($value);
$r = $this->activityReasonRepository->find($value);
return $this->stringHelper->localize($r->getCategory()->getName())
." > "
. $this->stringHelper->localize($r->getName());
;
break;
return sprintf(
"%s > %s",
$this->translatableStringHelper->localize($r->getCategory()->getName()),
$this->translatableStringHelper->localize($r->getName())
);
case 'categories':
$c = $this->categoryRepository->find($value);
$c = $this->activityReasonCategoryRepository->find($value);
return $this->stringHelper->localize($c->getName());
break;
// no need for a default : the default was already set above
return $this->translatableStringHelper->localize($c->getName());
}
};
@@ -222,12 +189,14 @@ class ActivityReasonAggregator implements AggregatorInterface,
// add select element
if ($data['level'] === 'reasons') {
return array('activity_reasons_id');
} elseif ($data['level'] === 'categories') {
return array ('activity_categories_id');
} else {
throw new \RuntimeException('the data provided are not recognised');
}
if ($data['level'] === 'categories') {
return array ('activity_categories_id');
}
throw new \RuntimeException('The data provided are not recognised.');
}
}

View File

@@ -1,61 +1,32 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityTypeAggregator implements AggregatorInterface
{
protected ActivityTypeRepository $activityTypeRepository;
/**
*
* @var EntityRepository
*/
protected $typeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
/**
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
const KEY = 'activity_type_aggregator';
public const KEY = 'activity_type_aggregator';
public function __construct(
EntityRepository $typeRepository,
TranslatableStringHelper $stringHelper
ActivityTypeRepository $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->typeRepository = $typeRepository;
$this->stringHelper = $stringHelper;
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function alterQuery(QueryBuilder $qb, $data)
@@ -64,7 +35,7 @@ class ActivityTypeAggregator implements AggregatorInterface
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
// add the "group by" part
$groupBy = $qb->addGroupBy(self::KEY);
$qb->addGroupBy(self::KEY);
}
/**
@@ -97,7 +68,7 @@ class ActivityTypeAggregator implements AggregatorInterface
public function getTitle()
{
return "Aggregate by activity type";
return 'Aggregate by activity type';
}
public function addRole()
@@ -108,17 +79,16 @@ class ActivityTypeAggregator implements AggregatorInterface
public function getLabels($key, array $values, $data): \Closure
{
// for performance reason, we load data from db only once
$this->typeRepository->findBy(array('id' => $values));
$this->activityTypeRepository->findBy(['id' => $values]);
return function($value): string {
if ($value === '_header') {
return 'Activity type';
}
/* @var $r \Chill\ActivityBundle\Entity\ActivityType */
$t = $this->typeRepository->find($value);
$t = $this->activityTypeRepository->find($value);
return $this->stringHelper->localize($t->getName());
return $this->translatableStringHelper->localize($t->getName());
};
}

View File

@@ -1,51 +1,26 @@
<?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query\Expr\Join;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
use Chill\MainBundle\Entity\User;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityUserAggregator implements AggregatorInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $em;
const KEY = 'activity_user_id';
function __construct(EntityManagerInterface $em)
{
$this->em = $em;
public const KEY = 'activity_user_id';
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository
) {
$this->userRepository = $userRepository;
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
@@ -53,9 +28,9 @@ class ActivityUserAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
// add select element
// add select element
$qb->addSelect(sprintf('IDENTITY(activity.user) AS %s', self::KEY));
// add the "group by" part
$qb->addGroupBy(self::KEY);
}
@@ -73,17 +48,14 @@ class ActivityUserAggregator implements AggregatorInterface
public function getLabels($key, $values, $data): \Closure
{
// preload users at once
$this->em->getRepository(User::class)
->findBy(['id' => $values]);
$this->userRepository->findBy(['id' => $values]);
return function($value) {
switch ($value) {
case '_header':
return 'activity user';
default:
return $this->em->getRepository(User::class)->find($value)
->getUsername();
if ($value === '_header') {
return 'activity user';
}
return $this->userRepository->find($value)->getUsername();
};
}
@@ -94,6 +66,6 @@ class ActivityUserAggregator implements AggregatorInterface
public function getTitle(): string
{
return "Aggregate by activity user";
return 'Aggregate by activity user';
}
}

View File

@@ -1,64 +1,40 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountActivity implements ExportInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
protected ActivityRepository $activityRepository;
public function __construct(
EntityManagerInterface $em
)
{
$this->entityManager = $em;
ActivityRepository $activityRepository
) {
$this->activityRepository = $activityRepository;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
}
public function getDescription()
{
return "Count activities by various parameters.";
return 'Count activities by various parameters.';
}
public function getTitle()
{
return "Count activities";
return 'Count activities';
}
public function getType()
@@ -68,26 +44,26 @@ class CountActivity implements ExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{
$qb = $this->entityManager->createQueryBuilder();
$centers = array_map(function($el) { return $el['center']; }, $acl);
$qb->select('COUNT(activity.id) as export_count_activity')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
;
$qb->where($qb->expr()->in('person.center', ':centers'))
->setParameter('centers', $centers)
;
$centers = array_map(static fn($el) => $el['center'], $acl);
$qb = $this
->activityRepository
->createQueryBuilder('activity')
->select('COUNT(activity.id) as export_count_activity')
->join('activity.person', 'person');
$qb
->where($qb->expr()->in('person.center', ':centers'))
->setParameter('centers', $centers);
return $qb;
}
public function supportsModifiers()
{
return array('person', 'activity');
return ['person', 'activity'];
}
public function requiredRole()
{
return new Role(ActivityStatsVoter::STATS);
@@ -95,7 +71,7 @@ class CountActivity implements ExportInterface
public function getAllowedFormattersTypes()
{
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR);
return [FormatterInterface::TYPE_TABULAR];
}
public function getLabels($key, array $values, $data)
@@ -103,19 +79,13 @@ class CountActivity implements ExportInterface
if ($key !== 'export_count_activity') {
throw new \LogicException("the key $key is not used by this export");
}
return function($value) {
return $value === '_header' ?
'Number of activities'
:
$value
;
};
return static fn($value) => $value === '_header' ? 'Number of activities' : $value;
}
public function getQueryKeys($data)
{
return array('export_count_activity');
return ['export_count_activity'];
}
public function getResult($qb, $data)

View File

@@ -1,31 +1,14 @@
<?php
/*
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ListInterface;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Scope;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Symfony\Component\Form\FormBuilderInterface;
@@ -37,33 +20,17 @@ use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Create a list for all activities
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ListActivity implements ListInterface
{
private ActivityRepository $activityRepository;
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
protected EntityManagerInterface $entityManager;
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected $fields = array(
protected array $fields = [
'id',
'date',
'durationTime',
@@ -75,32 +42,28 @@ class ListActivity implements ListInterface
'person_firstname',
'person_lastname',
'person_id'
);
];
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper
)
{
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelperInterface $translatableStringHelper,
ActivityRepository $activityRepository
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
}
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('fields', ChoiceType::class, array(
$builder->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => array_combine($this->fields, $this->fields),
'label' => 'Fields to include in export',
'constraints' => [new Callback(array(
'constraints' => [new Callback([
'callback' => function($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) {
$context->buildViolation('You must select at least one element')
@@ -108,19 +71,14 @@ class ListActivity implements ListInterface
->addViolation();
}
}
))]
));
])]
]);
}
/**
* {@inheritDoc}
*
* @return type
*/
public function getAllowedFormattersTypes()
{
return array(FormatterInterface::TYPE_LIST);
return [FormatterInterface::TYPE_LIST];
}
public function getDescription()
@@ -133,29 +91,32 @@ class ListActivity implements ListInterface
switch ($key)
{
case 'date' :
return function($value) {
if ($value === '_header') return 'date';
return static function($value) {
if ($value === '_header') {
return 'date';
}
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y');
};
case 'attendee':
return function($value) {
if ($value === '_header') return 'attendee';
return static function($value) {
if ($value === '_header') {
return 'attendee';
}
return $value ? 1 : 0;
};
case 'list_reasons' :
/* @var $activityReasonsRepository EntityRepository */
$activityRepository = $this->entityManager
->getRepository('ChillActivityBundle:Activity');
$activityRepository = $this->activityRepository;
return function($value) use ($activityRepository) {
if ($value === '_header') return 'activity reasons';
return function($value) use ($activityRepository): string {
if ($value === '_header') {
return 'activity reasons';
}
$activity = $activityRepository
->find($value);
$activity = $activityRepository->find($value);
return implode(", ", array_map(function(ActivityReason $r) {
@@ -168,21 +129,25 @@ class ListActivity implements ListInterface
};
case 'circle_name' :
return function($value) {
if ($value === '_header') return 'circle';
if ($value === '_header') {
return 'circle';
}
return $this->translatableStringHelper
->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode($value, true));
};
case 'type_name' :
return function($value) {
if ($value === '_header') return 'activity type';
if ($value === '_header') {
return 'activity type';
}
return $this->translatableStringHelper
->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode($value, true));
};
default:
return function($value) use ($key) {
if ($value === '_header') return $key;
return static function($value) use ($key) {
if ($value === '_header') {
return $key;
}
return $value;
};
@@ -209,14 +174,13 @@ class ListActivity implements ListInterface
return 'activity';
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(function($el) { return $el['center']; }, $acl);
// throw an error if any fields are present
if (!\array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields "
. "have been checked");
throw new InvalidArgumentException('Any fields have been checked.');
}
$qb = $this->entityManager->createQueryBuilder();
@@ -227,7 +191,6 @@ class ListActivity implements ListInterface
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
;
foreach ($this->fields as $f) {
if (in_array($f, $data['fields'])) {
@@ -269,8 +232,6 @@ class ListActivity implements ListInterface
}
}
return $qb;
}
@@ -281,7 +242,7 @@ class ListActivity implements ListInterface
public function supportsModifiers()
{
return array('activity', 'person');
return ['activity', 'person'];
}
}

View File

@@ -4,12 +4,13 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/**
* This export allow to compute stats on activity duration.
@@ -18,7 +19,7 @@ use Doctrine\ORM\EntityManagerInterface;
*/
class StatActivityDuration implements ExportInterface
{
protected EntityManagerInterface $entityManager;
private ActivityRepository $activityRepository;
public const SUM = 'sum';
@@ -30,13 +31,15 @@ class StatActivityDuration implements ExportInterface
/**
* @param string $action the stat to perform
*/
public function __construct(EntityManagerInterface $em, string $action = 'sum')
{
$this->entityManager = $em;
public function __construct(
ActivityRepository $activityRepository,
string $action = 'sum'
) {
$this->action = $action;
$this->activityRepository = $activityRepository;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
}
@@ -68,7 +71,7 @@ class StatActivityDuration implements ExportInterface
$acl
);
$qb = $this->entityManager->createQueryBuilder();
$qb = $this->activityRepository->createQueryBuilder('activity');
$select = null;
@@ -77,7 +80,6 @@ class StatActivityDuration implements ExportInterface
}
return $qb->select($select)
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join('person.center', 'center')
->where($qb->expr()->in('center', ':centers'))
@@ -96,7 +98,7 @@ class StatActivityDuration implements ExportInterface
public function getAllowedFormattersTypes()
{
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR);
return [FormatterInterface::TYPE_TABULAR];
}
public function getLabels($key, array $values, $data)

View File

@@ -1,25 +1,12 @@
<?php
/*
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType;
@@ -28,34 +15,24 @@ use Chill\MainBundle\Form\Type\Export\FilterType;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityDateFilter implements FilterInterface
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole()
{
return null;
}
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->between('activity.date', ':date_from',
$clause = $qb->expr()->between('activity.date', ':date_from',
':date_to');
if ($where instanceof Expr\Andx) {
@@ -63,7 +40,7 @@ class ActivityDateFilter implements FilterInterface
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('date_from', $data['date_from']);
$qb->setParameter('date_to', $data['date_to']);
@@ -74,35 +51,43 @@ class ActivityDateFilter implements FilterInterface
return 'activity';
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', DateType::class, array(
'label' => "Activities after this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('date_to', DateType::class, array(
'label' => "Activities before this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add(
'date_from',
DateType::class,
[
'label' => 'Activities after this date',
'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->add(
'date_to',
DateType::class,
[
'label' => 'Activities before this date',
'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */
$filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if ($enabled === true) {
// if the filter is enabled, add some validation
$form = $event->getForm();
$date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData();
// check that fields are not empty
if ($date_from === null) {
$form->get('date_from')->addError(new FormError(
@@ -113,8 +98,8 @@ class ActivityDateFilter implements FilterInterface
$form->get('date_to')->addError(new FormError(
$this->translator->trans('This field '
. 'should not be empty')));
}
}
// check that date_from is before date_to
if (
($date_from !== null && $date_to !== null)
@@ -132,17 +117,18 @@ class ActivityDateFilter implements FilterInterface
public function describeAction($data, $format = 'string')
{
return array(
"Filtered by date of activity: only between %date_from% and %date_to%",
array(
"%date_from%" => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
));
return [
'Filtered by date of activity: only between %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
]
];
}
public function getTitle()
{
return "Filtered by date activity";
return 'Filtered by date activity';
}
}

View File

@@ -1,25 +1,12 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -28,41 +15,24 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonFilter implements FilterInterface,
ExportElementValidatedInterface
class ActivityReasonFilter implements FilterInterface, ExportElementValidatedInterface
{
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $reasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityReasonRepository $activityReasonRepository;
public function __construct(
TranslatableStringHelper $helper,
EntityRepository $reasonRepository
TranslatableStringHelper $helper,
ActivityReasonRepository $activityReasonRepository
) {
$this->translatableStringHelper = $helper;
$this->reasonRepository = $reasonRepository;
$this->activityReasonRepository = $activityReasonRepository;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
@@ -75,12 +45,12 @@ class ActivityReasonFilter implements FilterInterface,
&&
!$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
)
OR
||
(! array_key_exists('activity', $join))
) {
$qb->add(
'join',
array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')),
'join',
array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')),
true
);
}
@@ -90,25 +60,25 @@ class ActivityReasonFilter implements FilterInterface,
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('selected_activity_reasons', $data['reasons']);
}
/**
* Check if a join between Activity and Reason is already defined
*
*
* @param Join[] $joins
* @return boolean
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
private function checkJoinAlreadyDefined(array $joins, $alias): bool
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
@@ -119,51 +89,47 @@ class ActivityReasonFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder)
{
//create a local copy of translatableStringHelper
$helper = $this->translatableStringHelper;
$builder->add('reasons', EntityType::class, array(
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function (ActivityReason $reason) use ($helper) {
return $helper->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) use ($helper) {
return $helper->localize($reason->getCategory()->getName());
},
$builder->add('reasons', EntityType::class, [
'class' => ActivityReason::class,
'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'multiple' => true,
'expanded' => false
));
]);
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation("At least one reason must be choosen")
$context
->buildViolation('At least one reason must be chosen')
->addViolation();
}
}
public function getTitle()
public function getTitle()
{
return 'Filter by reason';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function describeAction($data, $format = 'string')
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
function(ActivityReason $r) {
return "\"".$this->translatableStringHelper->localize($r->getName())."\"";
},
$this->reasonRepository->findBy(array('id' => $data['reasons']->toArray()))
);
return array("Filtered by reasons: only %list%",
["%list%" => implode(", ", $reasonsNames)]);
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
$this->activityReasonRepository->findBy(array('id' => $data['reasons']->toArray()))
);
return [
'Filtered by reasons: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
}
}

View File

@@ -1,67 +1,37 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\ActivityBundle\Entity\ActivityType;
/**
*
*
*/
class ActivityTypeFilter implements FilterInterface,
ExportElementValidatedInterface
class ActivityTypeFilter implements FilterInterface, ExportElementValidatedInterface
{
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $typeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityTypeRepository $activityTypeRepository;
public function __construct(
TranslatableStringHelper $helper,
EntityRepository $typeRepository
TranslatableStringHelperInterface $translatableStringHelper,
ActivityTypeRepository $activityTypeRepository
) {
$this->translatableStringHelper = $helper;
$this->typeRepository = $typeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityTypeRepository = $activityTypeRepository;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
@@ -72,14 +42,14 @@ class ActivityTypeFilter implements FilterInterface,
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('selected_activity_types', $data['types']);
}
/**
* Check if a join between Activity and Reason is already defined
*
*
* @param Join[] $joins
* @return boolean
*/
@@ -90,7 +60,7 @@ class ActivityTypeFilter implements FilterInterface,
return true;
}
}
return false;
}
@@ -101,48 +71,50 @@ class ActivityTypeFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder)
{
//create a local copy of translatableStringHelper
$helper = $this->translatableStringHelper;
$builder->add('types', EntityType::class, array(
'class' => ActivityType::class,
'choice_label' => function (ActivityType $type) use ($helper) {
return $helper->localize($type->getName());
},
'multiple' => true,
'expanded' => false
));
$builder->add(
'types',
EntityType::class,
[
'class' => ActivityType::class,
'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
'multiple' => true,
'expanded' => false
]
);
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['types'] === null || count($data['types']) === 0) {
$context->buildViolation("At least one type must be choosen")
$context
->buildViolation('At least one type must be chosen')
->addViolation();
}
}
public function getTitle()
public function getTitle()
{
return 'Filter by activity type';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function describeAction($data, $format = 'string')
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
function(ActivityType $t) {
return "\"".$this->translatableStringHelper->localize($t->getName())."\"";
},
$this->typeRepository->findBy(array('id' => $data['types']->toArray()))
);
return array("Filtered by activity type: only %list%",
["%list%" => implode(", ", $reasonsNames)]);
fn(ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
$this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
);
return [
'Filtered by activity type: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
}
}

View File

@@ -1,25 +1,14 @@
<?php
/*
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType;
@@ -29,78 +18,58 @@ use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
use Chill\PersonBundle\Export\Declarations;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonHavingActivityBetweenDateFilter implements FilterInterface,
ExportElementValidatedInterface
class PersonHavingActivityBetweenDateFilter implements FilterInterface, ExportElementValidatedInterface
{
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
*
* @var EntityRepository
*/
protected $activityReasonRepository;
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatorInterface $translator;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
EntityRepository $activityReasonRepository,
TranslatorInterface $translator
TranslatableStringHelper $translatableStringHelper,
ActivityReasonRepository $activityReasonRepository,
TranslatorInterface $translator
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->activityReasonRepository = $activityReasonRepository;
$this->translator = $translator;
$this->translator = $translator;
}
public function addRole()
{
return null;
}
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data)
{
// create a query for activity
$sqb = $qb->getEntityManager()->createQueryBuilder();
$sqb->select("person_person_having_activity.id")
->from("ChillActivityBundle:Activity", "activity_person_having_activity")
->join("activity_person_having_activity.person", "person_person_having_activity")
;
$sqb->select('person_person_having_activity.id')
->from('ChillActivityBundle:Activity', 'activity_person_having_activity')
->join('activity_person_having_activity.person', 'person_person_having_activity');
// add clause between date
$sqb->where("activity_person_having_activity.date BETWEEN "
. ":person_having_activity_between_date_from"
. " AND "
. ":person_having_activity_between_date_to");
$sqb->where('activity_person_having_activity.date BETWEEN '
. ':person_having_activity_between_date_from'
. ' AND '
. ':person_having_activity_between_date_to');
// add clause activity reason
$sqb->join('activity_person_having_activity.reasons',
'reasons_person_having_activity');
$sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
$sqb->andWhere(
$sqb->expr()->in(
'reasons_person_having_activity',
":person_having_activity_reasons")
);
$sqb->expr()->in(
'reasons_person_having_activity', ':person_having_activity_reasons'
)
);
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('person.id', $sqb->getDQL());
@@ -109,11 +78,11 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('person_having_activity_between_date_from',
$qb->setParameter('person_having_activity_between_date_from',
$data['date_from']);
$qb->setParameter('person_having_activity_between_date_to',
$qb->setParameter('person_having_activity_between_date_to',
$data['date_to']);
$qb->setParameter('person_having_activity_reasons', $data['reasons']);
}
@@ -123,51 +92,45 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
return Declarations::PERSON_IMPLIED_IN;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', DateType::class, array(
'label' => "Implied in an activity after this date",
$builder->add('date_from', DateType::class, [
'label' => 'Implied in an activity after this date',
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('date_to', DateType::class, array(
'label' => "Implied in an activity before this date",
]);
$builder->add('date_to', DateType::class, [
'label' => 'Implied in an activity before this date',
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('reasons', EntityType::class, array(
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function (ActivityReason $reason) {
return $this->translatableStringHelper
->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) {
return $this->translatableStringHelper
->localize($reason->getCategory()->getName());
},
]);
$builder->add('reasons', EntityType::class, [
'class' => ActivityReason::class,
'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
'group_by' => fn(ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'data' => $this->activityReasonRepository->findAll(),
'multiple' => true,
'expanded' => false,
'label' => "Activity reasons for those activities"
));
'label' => 'Activity reasons for those activities'
]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */
/* @var FormInterface $filterForm */
$filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if ($enabled === true) {
// if the filter is enabled, add some validation
$form = $event->getForm();
$date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData();
// check that fields are not empty
if ($date_from === null) {
$form->get('date_from')->addError(new FormError(
@@ -178,8 +141,8 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
$form->get('date_to')->addError(new FormError(
$this->translator->trans('This field '
. 'should not be empty')));
}
}
// check that date_from is before date_to
if (
($date_from !== null && $date_to !== null)
@@ -194,35 +157,37 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
}
});
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation("At least one reason must be choosen")
$context->buildViolation('At least one reason must be chosen')
->addViolation();
}
}
public function describeAction($data, $format = 'string')
{
return array(
"Filtered by person having an activity between %date_from% and "
. "%date_to% with reasons %reasons_name%",
array(
"%date_from%" => $data['date_from']->format('d-m-Y'),
return [
'Filtered by person having an activity between %date_from% and '
. '%date_to% with reasons %reasons_name%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
"%reasons_name%" => implode(", ", array_map(
function (ActivityReason $r) {
return '"'.$this->translatableStringHelper->
localize($r->getName()).'"';
},
$data['reasons']))
));
'%reasons_name%' => implode(
", ",
array_map(
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
$data['reasons']
)
)
]
];
}
public function getTitle()
{
return "Filtered by person having an activity in a period";
return 'Filtered by person having an activity in a period';
}
}

View File

@@ -1,56 +1,29 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\ActivityBundle\Entity\ActivityType;
/**
* Description of TranslatableActivityType
*
* @author Champs-Libres Coop
*/
class TranslatableActivityType extends AbstractType
{
protected TranslatableStringHelperInterface $translatableStringHelper;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected $activityTypeRepository;
protected ActivityTypeRepository $activityTypeRepository;
public function __construct(
TranslatableStringHelper $helper,
EntityRepository $activityTypeRepository
)
{
TranslatableStringHelperInterface $helper,
ActivityTypeRepository $activityTypeRepository
) {
$this->translatableStringHelper = $helper;
$this->activityTypeRepository = $activityTypeRepository;
}
@@ -65,22 +38,21 @@ class TranslatableActivityType extends AbstractType
return EntityType::class;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options) {
/* @var $qb \Doctrine\ORM\QueryBuilder */
public function buildForm(FormBuilderInterface $builder, array $options) {
/* @var QueryBuilder $qb */
$qb = $options['query_builder'];
if ($options['active_only'] === true) {
$qb->where($qb->expr()->eq('at.active', ':active'));
$qb->setParameter('active', true, \Doctrine\DBAL\Types\Types::BOOLEAN);
$qb->setParameter('active', true, Types::BOOLEAN);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'class' => 'ChillActivityBundle:ActivityType',
'class' => ActivityType::class,
'active_only' => true,
'query_builder' => $this->activityTypeRepository
->createQueryBuilder('at'),

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityReasonCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReasonCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReasonCategory[] findAll()
* @method ActivityReasonCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonCategoryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityReasonCategory::class);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityReason|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReason|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReason[] findAll()
* @method ActivityReason[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityReason::class);
}
}

View File

@@ -1,24 +1,6 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
@@ -29,10 +11,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null)
* @method AccompanyingPeriodParticipation[] findAll()
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
* @method Activity|null find($id, $lockMode = null, $lockVersion = null)
* @method Activity|null findOneBy(array $criteria, array $orderBy = null)
* @method Activity[] findAll()
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityRepository extends ServiceEntityRepository
{
@@ -42,12 +24,7 @@ class ActivityRepository extends ServiceEntityRepository
}
/**
* @param $person
* @param array $scopes
* @param string[] $orderBy
* @param int $limit
* @param int $offset
* @return array|Activity[]
* @return Activity[]
*/
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
{
@@ -63,8 +40,7 @@ class ActivityRepository extends ServiceEntityRepository
':person MEMBER OF a.persons'
)
)
->setParameter('person', $person)
;
->setParameter('person', $person);
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir);
@@ -72,17 +48,11 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()
->getResult();
return $qb->getQuery()->getResult();
}
/**
* @param AccompanyingPeriod $period
* @param array $scopes
* @param int|null $limit
* @param int|null $offset
* @param array|string[] $orderBy
* @return array|Activity[]
* @return Activity[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
{
@@ -92,8 +62,7 @@ class ActivityRepository extends ServiceEntityRepository
if (!$allowNullScope) {
$qb
->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes)
;
->setParameter('scopes', $scopes);
} else {
$qb
->where(
@@ -102,16 +71,14 @@ class ActivityRepository extends ServiceEntityRepository
$qb->expr()->isNull('a.scope')
)
)
->setParameter('scopes', $scopes)
;
->setParameter('scopes', $scopes);
}
$qb
->andWhere(
$qb->expr()->eq('a.accompanyingPeriod', ':period')
)
->setParameter('period', $period)
;
->setParameter('period', $period);
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir);
@@ -119,7 +86,6 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()
->getResult();
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityTypeCategory;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityTypeCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityTypeCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityTypeCategory[] findAll()
* @method ActivityTypeCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityTypeCategoryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityTypeCategory::class);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityType|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityType|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityType[] findAll()
* @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityTypeRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityType::class);
}
}

View File

@@ -11,9 +11,12 @@
</div>
<div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
<ul>
<ul class="list-unstyled">
<li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
{{ p.text }}
<span class="badge bg-primary" style="cursor: pointer;">
<i class="fa fa-plus fa-fw text-success"></i>
{{ p.text }}
</span>
</li>
</ul>
</div>

View File

@@ -4,7 +4,7 @@
<span class="chill_denomination">
{{ textCutted }}
</span>
<a class="fa fa-fw fa-times"
<a class="fa fa-fw fa-times text-danger text-decoration-none"
@click.prevent="$emit('remove', person)">
</a>
</span>

View File

@@ -27,14 +27,16 @@
{{ activity.type.name | localize_translatable_string }}
<ul class="small_in_title">
{% if activity.location and t.locationVisible %}
<li>
<abbr title="{{ 'location'|trans }}">{{ 'location'|trans ~ ': ' }}</abbr>
{# TODO {% if activity.location %}{{ activity.location }}{% endif %} #}
Domicile de l'usager
<span class="item-key">{{ 'location'|trans ~ ': ' }}</span>
<span>{{ activity.location.locationType.title|localize_translatable_string }}</span>
{{ activity.location.name }}
</li>
{% endif %}
{% if activity.user and t.userVisible %}
<li>
<abbr title="{{ 'Referrer'|trans }}">{{ 'Referrer'|trans ~ ': ' }}</abbr>
<span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
{{ activity.user.usernameCanonical }}
</li>
{% endif %}

View File

@@ -55,7 +55,7 @@
{% endif %}
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc' } %}
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc', 'badge_person': 'true' } %}
<h2 class="chill-red">{{ 'Activity data'|trans }}</h2>

View File

@@ -1,4 +1,2 @@
{{ dump(notification) }}
<a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a>

View File

@@ -79,8 +79,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
$metadataActivity = $this->em->getClassMetadata(Activity::class);
$associationMapping = $metadataActivity->getAssociationMapping('person');
$role = new Role('CHILL_ACTIVITY_SEE');
$reachableScopes = $this->helper->getReachableScopes($this->user,
$role, $person->getCenter());
$reachableScopes = $this->helper->getReachableScopes($this->user, $role->getRole(), $person->getCenter());
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
$scopes_ids = [];

View File

@@ -1,4 +1,7 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill.activity.timeline:
class: Chill\ActivityBundle\Timeline\TimelineActivityProvider
@@ -13,17 +16,14 @@ services:
- { name: chill.timeline, context: 'center' }
Chill\ActivityBundle\Menu\:
autowire: true
autoconfigure: true
resource: '../Menu/'
tags: ['chill.menu_builder']
Chill\ActivityBundle\Notification\:
autowire: true
autoconfigure: true
resource: '../Notification'
Chill\ActivityBundle\Security\Authorization\:
resource: '../Security/Authorization/'
autowire: true
autoconfigure: true
Chill\ActivityBundle\Repository\:
resource: '../Repository/'

View File

@@ -1,82 +1,56 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill.activity.export.count_activity:
class: Chill\ActivityBundle\Export\Export\CountActivity
arguments:
- "@doctrine.orm.entity_manager"
tags:
- { name: chill.export, alias: 'count_activity' }
chill.activity.export.sum_activity_duration:
class: Chill\ActivityBundle\Export\Export\StatActivityDuration
arguments:
- "@doctrine.orm.entity_manager"
- "sum"
tags:
- { name: chill.export, alias: 'sum_activity_duration' }
chill.activity.export.list_activity:
class: Chill\ActivityBundle\Export\Export\ListActivity
arguments:
- "@doctrine.orm.entity_manager"
- "@translator"
- "@chill.main.helper.translatable_string"
tags:
- { name: chill.export, alias: 'list_activity' }
chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.reason"
tags:
- { name: chill.export_filter, alias: 'activity_reason_filter' }
chill.activity.export.type_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.activity_type"
tags:
- { name: chill.export_filter, alias: 'activity_type_filter' }
chill.activity.export.date_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter
arguments:
- "@translator"
tags:
- { name: chill.export_filter, alias: 'activity_date_filter' }
chill.activity.export.person_having_an_activity_between_date_filter:
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.reason"
- "@translator"
tags:
- #0 register as a filter
name: chill.export_filter
alias: 'activity_person_having_ac_bw_date_filter'
chill.activity.export.reason_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
arguments:
- "@chill_activity.repository.reason_category"
- "@chill_activity.repository.reason"
- "@chill.main.helper.translatable_string"
tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator }
chill.activity.export.type_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator
arguments:
- "@chill_activity.repository.activity_type"
- "@chill.main.helper.translatable_string"
tags:
- { name: chill.export_aggregator, alias: activity_type_aggregator }
chill.activity.export.user_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
arguments:
$em: "@doctrine.orm.entity_manager"
tags:
- { name: chill.export_aggregator, alias: activity_user_aggregator }

View File

@@ -1,28 +1,12 @@
---
services:
chill_activity.repository.activity_type:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityType'
chill_activity.repository.reason:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityReason'
chill_activity.repository.reason_category:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityReasonCategory'
Chill\ActivityBundle\Repository\ActivityRepository:
tags: [doctrine.repository_service]
arguments:
- '@Doctrine\Persistence\ManagerRegistry'
chill_activity.repository.activity_type: '@Chill\ActivityBundle\Repository\ActivityTypeRepository'
chill_activity.repository.reason: '@Chill\ActivityBundle\Repository\ActivityReasonRepository'
chill_activity.repository.reason_category: '@Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository'
# This is not a repository but merely a service. It needs to be moved out for simplicity.
# The autowire and autoconfigure should be enabled globally and removed from the definition of the service.
# Once autoloaded, there is no need to alias it to the interface here, it will be done automatically by Symfony.
Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
autowire: true
autoconfigure: true

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Activity;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173555 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'activitytype.name',
'activitytypecategory.name'
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -44,13 +44,15 @@ class AsideActivityCategory
* @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children")
* @ORM\JoinColumn(nullable=true)
*/
private $parent;
private ?AsideActivityCategory $parent = null;
/**
* @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent")
*/
private $children;
private AsideActivityCategory $oldParent;
public function __construct()
{
$this->children = new ArrayCollection();

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Chill\AsideActivityBundle\Form;
use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
@@ -14,12 +16,11 @@ use Symfony\Component\Form\FormBuilderInterface;
final class AsideActivityCategoryType extends AbstractType
{
private CategoryRender $categoryRender;
protected $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper, CategoryRender $categoryRender)
{
$this->translatableStringHelper = $translatableStringHelper;
public function __construct(
CategoryRender $categoryRender
) {
$this->categoryRender = $categoryRender;
}

View File

@@ -25,18 +25,16 @@ use Symfony\Component\Templating\EngineInterface;
final class AsideActivityFormType extends AbstractType
{
protected array $timeChoices;
private array $timeChoices;
private TokenStorageInterface $storage;
private CategoryRender $categoryRender;
public function __construct (
TranslatableStringHelper $translatableStringHelper,
ParameterBagInterface $parameterBag,
TokenStorageInterface $storage,
CategoryRender $categoryRender
){
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
$this->translatableStringHelper = $translatableStringHelper;
$this->storage = $storage;
$this->categoryRender = $categoryRender;
}

View File

@@ -1,42 +1,34 @@
<?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\AMLI\BudgetBundle\Entity\Charge;
class ChargeType extends AbstractType
{
/**
*
* @var ConfigRepository
*/
protected $configRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected ConfigRepository $configRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper
ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
@@ -45,24 +37,23 @@ class ChargeType extends AbstractType
'placeholder' => 'Choose a charge type'
])
->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [
->add('comment', TextareaType::class, [
'required' => false
])
;
]);
if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [
'label' => 'Start of validity period'
]);
}
if ($options['show_end_date']) {
$builder->add('endDate', ChillDateType::class, [
'required' => false,
'label' => 'End of validity period'
]);
}
if ($options['show_help']) {
$builder->add('help', ChoiceType::class, [
'choices' => [
@@ -77,48 +68,39 @@ class ChargeType extends AbstractType
]);
}
}
private function getTypes()
{
$charges = $this->configRepository
->getChargesLabels();
// rewrite labels to filter in language
foreach ($charges as $key => $labels) {
$charges[$key] = $this->translatableStringHelper->localize($labels);
}
\asort($charges);
return \array_flip($charges);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Chill\AMLI\BudgetBundle\Entity\Charge',
'data_class' => Charge::class,
'show_start_date' => true,
'show_end_date' => true,
'show_help' => true
));
$resolver
->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean')
->setAllowedTypes('show_help', 'boolean')
;
->setAllowedTypes('show_help', 'boolean');
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'chill_amli_budgetbundle_charge';
}
}

View File

@@ -1,7 +1,10 @@
<?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -10,32 +13,22 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class ResourceType extends AbstractType
{
/**
*
* @var ConfigRepository
*/
protected $configRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected ConfigRepository $configRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper
ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
@@ -45,17 +38,16 @@ class ResourceType extends AbstractType
'label' => 'Resource element type'
])
->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [
->add('comment', TextareaType::class, [
'required' => false
])
;
]);
if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [
'label' => 'Start of validity period'
]);
}
if ($options['show_end_date']) {
$builder->add('endDate', ChillDateType::class, [
'required' => false,
@@ -63,25 +55,7 @@ class ResourceType extends AbstractType
]);
}
}
private function getTypes()
{
$resources = $this->configRepository
->getResourcesLabels();
// rewrite labels to filter in language
foreach ($resources as $key => $labels) {
$resources[$key] = $this->translatableStringHelper->localize($labels);
}
asort($resources);
return \array_flip($resources);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
@@ -89,20 +63,29 @@ class ResourceType extends AbstractType
'show_start_date' => true,
'show_end_date' => true
));
$resolver
->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean')
;
->setAllowedTypes('show_end_date', 'boolean');
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'chill_amli_budgetbundle_resource';
}
private function getTypes()
{
$resources = $this->configRepository
->getResourcesLabels();
// rewrite labels to filter in language
foreach ($resources as $key => $labels) {
$resources[$key] = $this->translatableStringHelper->localize($labels);
}
asort($resources);
return \array_flip($resources);
}
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Chill\CalendarBundle\DataFixtures\ORM;
use Chill\CalendarBundle\Entity\CalendarRange;
@@ -15,6 +17,10 @@ use Doctrine\Persistence\ObjectManager;
class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface
{
private UserRepository $userRepository;
public static array $references = [];
public function __construct(
UserRepository $userRepository
) {
@@ -31,8 +37,6 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
return ['calendar'];
}
public static $references = [];
public function load(ObjectManager $manager): void
{
$arr = range(-50, 50);

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Calendar;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173557 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_calendar.cancel_reason.name',
'chill_calendar.invite.status',
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -1,28 +1,27 @@
<?php
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Form\DataTransformer;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Symfony\Component\Form\DataTransformerInterface;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;
class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
/**
* @var ObjectManager
*/
private $om;
private ObjectManager $om;
private array $customField;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
$customFields = $this->om
->getRepository('ChillCustomFieldsBundle:CustomField')
->getRepository(CustomField::class)
->findAll();
// @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here?
$customFieldsLablels = array_map(
function($e) { return $e->getLabel(); },
$customFields);
@@ -36,20 +35,12 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{
echo $customFieldsJSON;
if($customFieldsJSON === null) { // lors de la creation
$customFieldsArray = array();
if($customFieldsJSON === null) {
$customFieldsArray = [];
} else {
$customFieldsArray = json_decode($customFieldsJSON,true);
$customFieldsArray = json_decode($customFieldsJSON, true, 512, JSON_THROW_ON_ERROR);
}
/*
echo "<br> - 4 - <br>";
var_dump($customFieldsArray);
echo "<br> - 5 - <br>";
*/
$customFieldsArrayRet = array();
foreach ($customFieldsArray as $key => $value) {
@@ -62,7 +53,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
} else {
$entityClass = substr($type, 10, -1);
}
$customFieldsArrayRet[$key] = $this->om
->getRepository('ChillCustomFieldsBundle:' . $entityClass)
->findOneById($value);
@@ -86,10 +77,10 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{
/*
echo "<br> - - 7 - <br>";
var_dump(array_keys($customFieldsArray));
echo "<br> - - 8 - <br>";
var_dump(array_keys($this->customField));
@@ -112,7 +103,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
//$entityClass = substr($type, 10, -1);
//echo $entityClasss;
if(strpos($type, 'ManyToOnePersist') === 0) {
// PEUT ETRE A FAIRE SI SEULEMENT $value->getId() ne renvoie rien...
// PEUT ETRE A FAIRE SI SEULEMENT $value->getId() ne renvoie rien...
//
//
$this->om->persist($value); // pas bon ici
@@ -121,7 +112,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
// et faire le persist qd fait sur l'obj parent
// regarder : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html
// ou : http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
// dans yml :
// dans yml :
// lifecycleCallbacks:
// prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
$this->om->flush(); // sinon l'id pose pbm
@@ -142,4 +133,4 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
return json_encode($customFieldsArrayRet);
}
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Encoder;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class DocGenEncoder implements \Symfony\Component\Serializer\Encoder\EncoderInterface
{
/**
* @inheritDoc
*/
public function encode($data, string $format, array $context = [])
{
if (!$this->isAssociative($data)) {
throw new UnexpectedValueException("Only associative arrays are allowed; lists are not allowed");
}
$result = [];
$this->recusiveEncoding($data, $result, '');
return $result;
}
private function recusiveEncoding(array $data, array &$result, $path)
{
if ($this->isAssociative($data)) {
foreach ($data as $key => $value) {
if (\is_array($value)) {
$this->recusiveEncoding($value, $result, $this->canonicalizeKey($path, $key));
} else {
$result[$this->canonicalizeKey($path, $key)] = $value;
}
}
} else {
foreach ($data as $elem) {
if (!$this->isAssociative($elem)) {
throw new UnexpectedValueException(sprintf("Embedded loops are not allowed. See data under %s path", $path));
}
$sub = [];
$this->recusiveEncoding($elem, $sub, '');
$result[$path][] = $sub;
}
}
}
private function canonicalizeKey(string $path, string $key): string
{
return $path === '' ? $key : $path.'_'.$key;
}
private function isAssociative(array $data)
{
$keys = \array_keys($data);
return $keys !== \array_keys($keys);
}
/**
* @inheritDoc
*/
public function supportsEncoding(string $format)
{
return $format === 'docgen';
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Helper;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class NormalizeNullValueHelper
{
private NormalizerInterface $normalizer;
public function __construct(NormalizerInterface $normalizer)
{
$this->normalizer = $normalizer;
}
public function normalize(array $attributes, string $format = 'docgen', ?array $context = [])
{
$data = [];
foreach ($attributes as $key => $class) {
if (is_numeric($key)) {
$data[$class] = '';
} else {
switch ($class) {
case 'array':
case 'bool':
case 'double':
case 'float':
case 'int':
case 'resource':
case 'string':
case 'null':
$data[$key] = '';
break;
default:
$data[$key] = $this->normalizer->normalize(null, $format, \array_merge(
$context,
['docgen:expects' => $class]
));
break;
}
}
}
return $data;
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DocGenObjectNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private ClassMetadataFactoryInterface $classMetadataFactory;
private PropertyAccessor $propertyAccess;
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
{
$this->classMetadataFactory = $classMetadataFactory;
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
}
/**
* @inheritDoc
*/
public function normalize($object, string $format = null, array $context = [])
{
$classMetadataKey = $object ?? $context['docgen:expects'];
if (!$this->classMetadataFactory->hasMetadataFor($classMetadataKey)) {
throw new LogicException(sprintf("This object does not have metadata: %s. Add groups on this entity to allow to serialize with the format %s and groups %s", is_object($object) ? get_class($object) : $context['docgen:expects'], $format, \implode(', ', $context['groups'])));
}
$metadata = $this->classMetadataFactory->getMetadataFor($classMetadataKey);
$expectedGroups = \array_key_exists(AbstractNormalizer::GROUPS, $context) ?
\is_array($context[AbstractNormalizer::GROUPS]) ? $context[AbstractNormalizer::GROUPS] : [$context[AbstractNormalizer::GROUPS]]
: [];
$attributes = \array_filter(
$metadata->getAttributesMetadata(),
function (AttributeMetadata $a) use ($expectedGroups) {
foreach ($a->getGroups() as $g) {
if (\in_array($g, $expectedGroups, true)) {
return true;
}
}
return false;
});
if (null === $object) {
return $this->normalizeNullData($format, $context, $metadata, $attributes);
}
return $this->normalizeObject($object, $format, $context, $expectedGroups, $metadata, $attributes);
}
/**
* @param string $format
* @param array $context
* @param array $expectedGroups
* @param ClassMetadata $metadata
* @param array|AttributeMetadata[] $attributes
*/
private function normalizeNullData(string $format, array $context, ClassMetadata $metadata, array $attributes): array
{
$keys = [];
foreach ($attributes as $attribute) {
$key = $attribute->getSerializedName() ?? $attribute->getName();
$keys[$key] = $this->getExpectedType($attribute, $metadata->getReflectionClass());
}
$normalizer = new NormalizeNullValueHelper($this->normalizer);
return $normalizer->normalize($keys, $format, $context);
}
/**
* @param $object
* @param $format
* @param array $context
* @param array $expectedGroups
* @param ClassMetadata $metadata
* @param array|AttributeMetadata[] $attributes
* @return array
* @throws ExceptionInterface
*/
private function normalizeObject($object, $format, array $context, array $expectedGroups, ClassMetadata $metadata, array $attributes)
{
$data = [];
$reflection = $metadata->getReflectionClass();
foreach ($attributes as $attribute) {
/** @var AttributeMetadata $attribute */
$value = $this->propertyAccess->getValue($object, $attribute->getName());
$key = $attribute->getSerializedName() ?? $attribute->getName();
if (is_object($value)) {
$data[$key] =
$this->normalizer->normalize($value, $format, \array_merge(
$context, $attribute->getNormalizationContextForGroups($expectedGroups)
));
} elseif (null === $value) {
$data[$key] = $this->normalizeNullOutputValue($format, $context, $attribute, $reflection);
} else {
$data[$key] = (string) $value;
}
}
return $data;
}
private function getExpectedType(AttributeMetadata $attribute, \ReflectionClass $reflection): string
{
// we have to get the expected content
if ($reflection->hasProperty($attribute->getName())) {
$type = $reflection->getProperty($attribute->getName())->getType();
} elseif ($reflection->hasMethod($attribute->getName())) {
$type = $reflection->getMethod($attribute->getName())->getReturnType();
} else {
throw new \LogicException(sprintf(
"Could not determine how the content is determined for the attribute %s. Add attribute property only on property or method", $attribute->getName()
));
}
if (null === $type) {
throw new \LogicException(sprintf(
"Could not determine the type for this attribute: %s. Add a return type to the method or property declaration", $attribute->getName()
));
}
return $type->getName();
}
/**
*/
private function normalizeNullOutputValue($format, array $context, AttributeMetadata $attribute, \ReflectionClass $reflection)
{
$type = $this->getExpectedType($attribute, $reflection);
switch ($type) {
case 'array':
case 'bool':
case 'double':
case 'float':
case 'int':
case 'resource':
case 'string':
return '';
default:
return $this->normalizer->normalize(
null,
$format,
\array_merge(
$context,
['docgen:expects' => $type]
)
);
}
}
/**
* @inheritDoc
*/
public function supportsNormalization($data, string $format = null): bool
{
return $format === 'docgen' && (is_object($data) || null === $data);
}
}

View File

@@ -8,3 +8,10 @@ services:
autowire: true
autoconfigure: true
resource: '../Repository/'
Chill\DocGeneratorBundle\Serializer\Normalizer\:
autowire: true
autoconfigure: true
resource: '../Serializer/Normalizer/'
tags:
- { name: 'serializer.normalizer', priority: -152 }

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocGenerator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173556 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_docgen_template.name'
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Chill\DocGeneratorBundle\Tests\Serializer\Encoder;
use Chill\DocGeneratorBundle\Serializer\Encoder\DocGenEncoder;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class DocGenEncoderTest extends TestCase
{
private DocGenEncoder $encoder;
protected function setUp()
{
parent::setUp();
$this->encoder = new DocGenEncoder();
}
/**
* @dataProvider generateEncodeData
*/
public function testEncode($expected, $data, string $msg)
{
$generated = $this->encoder->encode($data, 'docgen');
$this->assertEquals($expected, $generated, $msg);
}
public function testEmbeddedLoopsThrowsException()
{
$this->expectException(UnexpectedValueException::class);
$data = [
'data' => [
['item' => 'one'],
[
'embedded' => [
[
['subitem' => 'two'],
['subitem' => 'three']
]
]
],
]
];
$this->encoder->encode($data, 'docgen');
}
public function generateEncodeData()
{
yield [ ['tests' => 'ok'], ['tests' => 'ok'], "A simple test with a simple array"];
yield [
// expected:
['item_subitem' => 'value'],
// data:
['item' => ['subitem' => 'value']],
"A test with multidimensional array"
];
yield [
// expected:
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
// data:
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
"a list of items"
];
yield [
// expected:
[ 'data' => [['item_subitem' => 'alpha'], ['item' => 'two']] ],
// data:
[ 'data' => [['item' => ['subitem' => 'alpha']], ['item' => 'two'] ] ],
"a list of items with multidimensional array inside item"
];
yield [
// expected:
[
'persons' => [
[
'firstname' => 'Jonathan',
'lastname' => 'Dupont',
'dateOfBirth_long' => '16 juin 1981',
'dateOfBirth_short' => '16/06/1981',
'father_firstname' => 'Marcel',
'father_lastname' => 'Dupont',
'father_dateOfBirth_long' => '10 novembre 1953',
'father_dateOfBirth_short' => '10/11/1953'
],
]
],
// data:
[
'persons' => [
[
'firstname' => 'Jonathan',
'lastname' => 'Dupont',
'dateOfBirth' => [ 'long' => '16 juin 1981', 'short' => '16/06/1981'],
'father' => [
'firstname' => 'Marcel',
'lastname' => 'Dupont',
'dateOfBirth' => ['long' => '10 novembre 1953', 'short' => '10/11/1953']
]
],
]
],
"a longer list, with near real data inside and embedded associative arrays"
];
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DocGenObjectNormalizerTest extends KernelTestCase
{
private NormalizerInterface $normalizer;
protected function setUp()
{
parent::setUp();
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
}
public function testNormalizationBasic()
{
$user = new User();
$user->setUsername('User Test');
$user->setMainCenter($center = new Center());
$center->setName('test');
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
$expected = [
'label' => 'User Test',
'email' => '',
'mainCenter' => [
'name' => 'test'
]
];
$this->assertEquals($expected, $normalized, "test normalization fo an user");
}
public function testNormalizeWithNullValueEmbedded()
{
$user = new User();
$user->setUsername('User Test');
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
$expected = [
'label' => 'User Test',
'email' => '',
'mainCenter' => [
'name' => ''
]
];
$this->assertEquals($expected, $normalized, "test normalization fo an user with null center");
}
public function testNormalizeNullObjectWithObjectEmbedded()
{
$normalized = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'],
'docgen:expects' => User::class,
]);
$expected = [
'label' => '',
'email' => '',
'mainCenter' => [
'name' => ''
]
];
$this->assertEquals($expected, $normalized, "test normalization for a null user");
}
}

View File

@@ -1,44 +1,19 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres SCRLFS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\DocStoreBundle\Security\Authorization;
use App\Security\Authorization\VoterHelperFactory;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Role\Role;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Security;
/**
*
*/
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocStore;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173558 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_doc.document_category.name',
'chill_doc.stored_object.key',
'chill_doc.stored_object.iv',
'chill_doc.stored_object.datas',
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -1,50 +1,46 @@
<?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Request;
use Chill\AMLI\FamilyMembersBundle\Repository\FamilyMemberRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter;
use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Translation\TranslatorInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Annotation\Route;
class FamilyMemberController extends Controller
class FamilyMemberController extends AbstractController
{
/**
*
* @var EntityManagerInterface
*/
protected $em;
/**
*
* @var TranslatorInterface
*/
protected $translator;
/**
*
* @var LoggerInterface
*/
protected $chillMainLogger;
private EntityManagerInterface $em;
protected TranslatorInterface $translator;
protected LoggerInterface $chillMainLogger;
private FamilyMemberRepository $familyMemberRepository;
public function __construct(
EntityManagerInterface $em,
EntityManagerInterface $entityManager,
TranslatorInterface $translator,
LoggerInterface $chillMainLogger
LoggerInterface $chillMainLogger,
FamilyMemberRepository $familyMemberRepository
) {
$this->em = $em;
$this->em = $entityManager;
$this->translator = $translator;
$this->chillMainLogger = $chillMainLogger;
$this->familyMemberRepository = $familyMemberRepository;
}
/**
* @Route(
* "{_locale}/family-members/family-members/by-person/{id}",
@@ -54,15 +50,13 @@ class FamilyMemberController extends Controller
public function indexAction(Person $person)
{
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person);
$familyMembers = $this->em
->getRepository(FamilyMember::class)
->findByPerson($person);
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', array(
$familyMembers = $this->familyMemberRepository->findByPerson($person);
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', [
'person' => $person,
'familyMembers' => $familyMembers
));
]);
}
/**
@@ -73,33 +67,30 @@ class FamilyMemberController extends Controller
*/
public function newAction(Person $person, Request $request)
{
$familyMember = (new FamilyMember())
->setPerson($person)
;
$familyMember = (new FamilyMember())->setPerson($person);
$this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember);
$form = $this->createForm(FamilyMemberType::class, $familyMember);
$form->add('submit', SubmitType::class);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($familyMember);
$em->flush();
if ($form->isSubmitted() && $form->isValid()) {
$this->em->persist($familyMember);
$this->em->flush();
$this->addFlash('success', $this->translator->trans('Family member created'));
return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $person->getId()
]);
}
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', array(
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', [
'form' => $form->createView(),
'person' => $person
));
]);
}
/**
@@ -111,79 +102,73 @@ class FamilyMemberController extends Controller
public function editAction(FamilyMember $familyMember, Request $request)
{
$this->denyAccessUnlessGranted(FamilyMemberVoter::UPDATE, $familyMember);
$form = $this->createForm(FamilyMemberType::class, $familyMember);
$form->add('submit', SubmitType::class);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->flush();
if ($form->isSubmitted() && $form->isValid()) {
$this->em->flush();
$this->addFlash('success', $this->translator->trans('Family member updated'));
return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $familyMember->getPerson()->getId()
]);
}
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', array(
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', [
'familyMember' => $familyMember,
'form' => $form->createView(),
'person' => $familyMember->getPerson()
));
]);
}
/**
*
*
* @Route(
* "{_locale}/family-members/family-members/{id}/delete",
* name="chill_family_members_family_members_delete"
* )
*
* @param FamilyMember $familyMember
* @param Request $request
* @return \Symfony\Component\BrowserKit\Response
*/
public function deleteAction(FamilyMember $familyMember, Request $request)
public function deleteAction(FamilyMember $familyMember, Request $request): Response
{
$this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not '
. 'allowed to delete this family membership');
$form = $this->createDeleteForm($id);
$form = $this->createDeleteForm();
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
if ($form->isValid()) {
$this->chillMainLogger->notice("A family member has been removed", array(
$this->chillMainLogger->notice("A family member has been removed", [
'by_user' => $this->getUser()->getUsername(),
'family_member_id' => $familyMember->getId(),
'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(),
'link' => $familyMember->getLink()
));
]);
$em = $this->getDoctrine()->getManager();
$em->remove($familyMember);
$em->flush();
$this->em->remove($familyMember);
$this->em->flush();
$this->addFlash('success', $this->translator
->trans("The family member has been successfully removed."));
return $this->redirectToRoute('chill_family_members_family_members_index', array(
return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $familyMember->getPerson()->getId()
));
]);
}
}
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', array(
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', [
'familyMember' => $familyMember,
'delete_form' => $form->createView()
));
]);
}
/**
* @Route(
* "{_locale}/family-members/family-members/{id}/view",
@@ -193,24 +178,21 @@ class FamilyMemberController extends Controller
public function viewAction(FamilyMember $familyMember)
{
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember);
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', array(
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', [
'familyMember' => $familyMember
));
]);
}
/**
* Creates a form to delete a help request entity by id.
*
* @param mixed $id The entity id
*
* @return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
private function createDeleteForm(): FormInterface
{
return $this->createFormBuilder()
return $this
->createFormBuilder()
->setMethod(Request::METHOD_DELETE)
->add('submit', SubmitType::class, array('label' => 'Delete'))
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm()
;
}

View File

@@ -1,17 +1,32 @@
<?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Repository;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* FamilyMemberRepository
*
* @method FamilyMember|null find($id, $lockMode = null, $lockVersion = null)
* @method FamilyMember|null findOneBy(array $criteria, array $orderBy = null)
* @method FamilyMember[] findAll()
* @method FamilyMember[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class FamilyMemberRepository extends \Doctrine\ORM\EntityRepository
class FamilyMemberRepository extends ServiceEntityRepository
{
public function findActiveByPerson(Person $person)
public function __construct(ManagerRegistry $registry)
{
return $this->findBy([ 'person' => $person ]);
parent::__construct($registry, FamilyMember::class);
}
/**
* @return FamilyMember[]
*/
public function findByPerson(Person $person): array
{
return $this->findBy(['person' => $person]);
}
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -14,94 +16,77 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Translation\TranslatorInterface;
class AbstractCRUDController extends AbstractController
abstract class AbstractCRUDController extends AbstractController
{
/**
* The crud configuration
* The crud configuration
*
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
*
* @var array
*/
protected array $crudConfig = [];
/**
* get the instance of the entity with the given id
*
* @param string $id
* @return object
*
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
*/
protected function getEntity($action, $id, Request $request): object
protected function getEntity($action, string $id, Request $request): object
{
$e = $this->getDoctrine()
$e = $this
->getDoctrine()
->getRepository($this->getEntityClass())
->find($id);
if (NULL === $e) {
if (null === $e) {
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
}
return $e;
}
/**
* Create an entity.
*
* @param string $action
* @param Request $request
* @return object
*/
protected function createEntity(string $action, Request $request): object
{
$type = $this->getEntityClass();
return new $type;
$class = $this->getEntityClass();
return new $class;
}
/**
* Count the number of entities
*
* By default, count all entities. You can customize the query by
* By default, count all entities. You can customize the query by
* using the method `customizeQuery`.
*
* @param string $action
* @param Request $request
* @return int
*/
protected function countEntities(string $action, Request $request, $_format): int
{
return $this->buildQueryEntities($action, $request)
->select('COUNT(e)')
->getQuery()
->getSingleScalarResult()
;
->getSingleScalarResult();
}
/**
* Query the entity.
*
*
* By default, get all entities. You can customize the query by using the
* method `customizeQuery`.
*
*
* The method `orderEntity` is called internally to order entities.
*
*
* It returns, by default, a query builder.
*
*/
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
{
$query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage());
// allow to order queries and return the new query
return $this->orderQuery($action, $query, $request, $paginator, $_format);
}
/**
* Add ordering fields in the query build by self::queryEntities
*
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{
@@ -112,14 +97,12 @@ class AbstractCRUDController extends AbstractController
* Build the base query for listing all entities.
*
* This method is used internally by `countEntities` `queryEntities`
*
*
* This base query does not contains any `WHERE` or `SELECT` clauses. You
* can add some by using the method `customizeQuery`.
*
* The alias for the entity is "e".
*
* @param string $action
* @param Request $request
*
* @return QueryBuilder
*/
protected function buildQueryEntities(string $action, Request $request)
@@ -127,8 +110,7 @@ class AbstractCRUDController extends AbstractController
$qb = $this->getDoctrine()->getManager()
->createQueryBuilder()
->select('e')
->from($this->getEntityClass(), 'e')
;
->from($this->getEntityClass(), 'e');
$this->customizeQuery($action, $request, $qb);
@@ -138,55 +120,54 @@ class AbstractCRUDController extends AbstractController
protected function customizeQuery(string $action, Request $request, $query): void {}
/**
* Get the result of the query
* Get the result of the query.
*/
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
{
return $query->getQuery()->getResult();
}
protected function onPreIndex(string $action, Request $request, string $_format): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{
{
return null;
}
/**
* method used by indexAction
* Method used by indexAction.
*/
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{
return null;
}
/**
* Method used by indexAction.
*/
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
{
return null;
}
/**
* method used by indexAction
* Method used by indexAction.
*/
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
{
return null;
}
/**
* Get the complete FQDN of the class
*
* @return string the complete fqdn of the class
* Get the FQDN of the class.
*
* @return class-string The FQDN of the class
*/
protected function getEntityClass(): string
{
return $this->crudConfig['class'];
}
/**
* called on post fetch entity
* Called on post fetch entity.
*/
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
{
@@ -194,7 +175,7 @@ class AbstractCRUDController extends AbstractController
}
/**
* Called on post check ACL
* Called on post check ACL.
*/
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{
@@ -203,23 +184,23 @@ class AbstractCRUDController extends AbstractController
/**
* check the acl. Called by every action.
*
* By default, check the role given by `getRoleFor` for the value given in
*
* By default, check the role given by `getRoleFor` for the value given in
* $entity.
*
*
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
* if not accessible.
*
*
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
*/
protected function checkACL(string $action, Request $request, string $_format, $entity = null)
{
// @TODO: Implements abstract getRoleFor method or do it in the interface.
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
}
/**
*
* @return string the crud name
* @return string The crud name.
*/
protected function getCrudName(): string
{
@@ -230,7 +211,7 @@ class AbstractCRUDController extends AbstractController
{
return $this->crudConfig['actions'][$action];
}
/**
* Set the crud configuration
*
@@ -241,9 +222,6 @@ class AbstractCRUDController extends AbstractController
$this->crudConfig = $config;
}
/**
* @return PaginatorFactory
*/
protected function getPaginatorFactory(): PaginatorFactory
{
return $this->container->get('chill_main.paginator_factory');
@@ -254,9 +232,6 @@ class AbstractCRUDController extends AbstractController
return $this->get('validator');
}
/**
* @return array
*/
public static function getSubscribedServices(): array
{
return \array_merge(

View File

@@ -3,6 +3,7 @@
namespace Chill\MainBundle;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\MainBundle\Search\SearchApiInterface;
use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
use Chill\MainBundle\Security\ProvideRoleInterface;
@@ -41,6 +42,8 @@ class ChillMainBundle extends Bundle
->addTag('chill_main.scope_resolver');
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
->addTag('chill.render_entity');
$container->registerForAutoconfiguration(SearchApiInterface::class)
->addTag('chill.search_api_provider');
$container->addCompilerPass(new SearchableServicesCompilerPass());
$container->addCompilerPass(new ConfigConsistencyCompilerPass());

View File

@@ -1,7 +1,10 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Command;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -23,101 +26,51 @@ use League\Csv\Writer;
class ChillImportUsersCommand extends Command
{
/**
*
* @var EntityManagerInterface
*/
protected $em;
/**
*
* @var ValidatorInterface
*/
protected $validator;
/**
*
* @var LoggerInterface
*/
protected $logger;
/**
*
* @var UserPasswordEncoderInterface
*/
protected $passwordEncoder;
/**
*
* @var \Chill\MainBundle\Repository\UserRepository
*/
protected $userRepository;
/**
*
* @var bool
*/
protected $doChanges = true;
/**
*
* @var OutputInterface
*/
protected $tempOutput;
/**
*
* @var InputInterface
*/
protected $tempInput;
protected EntityManagerInterface $em;
protected ValidatorInterface $validator;
protected LoggerInterface $logger;
protected UserPasswordEncoderInterface $passwordEncoder;
protected UserRepository $userRepository;
protected bool $doChanges = true;
protected OutputInterface $tempOutput;
protected InputInterface $tempInput;
/**
* Centers and aliases.
*
*
* key are aliases, values are an array of centers
*
* @var array
*/
protected $centers = [];
/**
*
* @var array
*/
protected $permissionGroups = [];
/**
*
* @var array
*/
protected $groupCenters = [];
/**
*
* @var Writer
*/
protected $output = null;
protected array $centers;
protected array $permissionGroups;
protected array $groupCenters;
protected Writer $output;
public function __construct(
EntityManagerInterface $em,
LoggerInterface $logger,
UserPasswordEncoderInterface $passwordEncoder,
ValidatorInterface $validator
ValidatorInterface $validator,
UserRepository $userRepository
) {
$this->em = $em;
$this->passwordEncoder = $passwordEncoder;
$this->validator = $validator;
$this->logger = $logger;
$this->userRepository = $em->getRepository(User::class);
$this->userRepository = $userRepository;
parent::__construct('chill:main:import-users');
}
protected function configure()
{
$this
@@ -126,25 +79,24 @@ class ChillImportUsersCommand extends Command
->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`')
->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes')
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file')
;
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->tempOutput = $output;
$this->tempInput = $input;
if ($input->getOption('dry-run')) {
$this->doChanges = false;
}
$this->prepareWriter();
if ($input->hasOption('grouping-centers')) {
$this->prepareGroupingCenters();
}
try {
$this->loadUsers();
}
@@ -152,19 +104,19 @@ class ChillImportUsersCommand extends Command
throw $e;
}
}
protected function prepareWriter()
{
$this->output = $output = Writer::createFromPath($this->tempInput
->getOption('csv-dump'), 'a+');
$output->insertOne([
'email',
'username',
'id'
]);
}
protected function appendUserToFile(User $user)
{
$this->output->insertOne( [
@@ -173,35 +125,35 @@ class ChillImportUsersCommand extends Command
$user->getId()
]);
}
protected function loadUsers()
{
$reader = Reader::createFromPath($this->tempInput->getArgument('csvfile'));
$reader->setHeaderOffset(0);
foreach ($reader->getRecords() as $line => $r) {
$this->logger->debug("starting handling new line", [
'line' => $line
]);
if ($this->doesUserExists($r)) {
$this->tempOutput->writeln(sprintf("User with username '%s' already "
. "exists, skipping", $r["username"]));
$this->logger->info("One user already exists, skipping creation", [
'username_in_file' => $r['username'],
'email_in_file' => $r['email'],
'line' => $line
]);
continue;
}
$user = $this->createUser($line, $r);
$this->appendUserToFile($user);
}
}
protected function doesUserExists($data)
{
if ($this->userRepository->countByUsernameOrEmail($data['username']) > 0) {
@@ -211,10 +163,10 @@ class ChillImportUsersCommand extends Command
if ($this->userRepository->countByUsernameOrEmail($data['email']) > 0) {
return true;
}
return false;
}
protected function createUser($offset, $data)
{
$user = new User();
@@ -222,41 +174,41 @@ class ChillImportUsersCommand extends Command
->setEmail(\trim($data['email']))
->setUsername(\trim($data['username']))
->setEnabled(true)
->setPassword($this->passwordEncoder->encodePassword($user,
->setPassword($this->passwordEncoder->encodePassword($user,
\bin2hex(\random_bytes(32))))
;
$errors = $this->validator->validate($user);
if ($errors->count() > 0) {
$errorMessages = $this->concatenateViolations($errors);
$this->tempOutput->writeln(sprintf("%d errors found with user with username \"%s\" at line %d", $errors->count(), $data['username'], $offset));
$this->tempOutput->writeln($errorMessages);
throw new \RuntimeException("Found errors while creating an user. "
. "Watch messages in command output");
}
$pgs = $this->getPermissionGroup($data['permission group']);
$centers = $this->getCenters($data['center']);
foreach($pgs as $pg) {
foreach ($centers as $center) {
$groupcenter = $this->createOrGetGroupCenter($center, $pg);
if (FALSE === $user->getGroupCenters()->contains($groupcenter)) {
$user->addGroupCenter($groupcenter);
}
}
}
if ($this->doChanges) {
$this->em->persist($user);
$this->em->flush();
}
$this->logger->notice("Create user", [
'username' => $user->getUsername(),
'id' => $user->getId(),
@@ -265,65 +217,58 @@ class ChillImportUsersCommand extends Command
return $user;
}
protected function getPermissionGroup($alias)
{
if (\array_key_exists($alias, $this->permissionGroups)) {
return $this->permissionGroups[$alias];
}
$permissionGroupsByName = [];
foreach($this->em->getRepository(PermissionsGroup::class)
->findAll() as $permissionGroup) {
$permissionGroupsByName[$permissionGroup->getName()] = $permissionGroup;
}
if (count($permissionGroupsByName) === 0) {
throw new \RuntimeException("no permission groups found. Create them "
. "before importing users");
}
$question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?",
$question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?",
\array_keys($permissionGroupsByName));
$question
->setMultiselect(true)
->setAutocompleterValues(\array_keys($permissionGroupsByName))
->setNormalizer(function($value) {
if (NULL === $value) { return ''; }
return \trim($value);
})
;
$helper = $this->getHelper('question');
$keys = $helper->ask($this->tempInput, $this->tempOutput, $question);
$this->tempOutput->writeln("You have chosen ".\implode(", ", $keys));
if ($helper->ask($this->tempInput, $this->tempOutput,
if ($helper->ask($this->tempInput, $this->tempOutput,
new ConfirmationQuestion("Are you sure ?", true))) {
foreach ($keys as $key) {
$this->permissionGroups[$alias][] = $permissionGroupsByName[$key];
}
return $this->permissionGroups[$alias];
} else {
$this->logger->error("Error while responding to a a question");
$this->tempOutput("Ok, I accept, but I do not know what to do. Please try again.");
throw new \RuntimeException("Error while responding to a question");
}
$this->logger->error('Error while responding to a a question');
$this->tempOutput->writeln('Ok, I accept, but I do not know what to do. Please try again.');
throw new \RuntimeException('Error while responding to a question');
}
/**
*
* @param Center $center
* @param \Chill\MainBundle\Command\PermissionGroup $pg
* @return GroupCenter
*/
protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter
{
if (\array_key_exists($center->getId(), $this->groupCenters)) {
@@ -331,36 +276,36 @@ class ChillImportUsersCommand extends Command
return $this->groupCenters[$center->getId()][$pg->getId()];
}
}
$repository = $this->em->getRepository(GroupCenter::class);
$groupCenter = $repository->findOneBy(array(
'center' => $center,
'permissionsGroup' => $pg
));
if ($groupCenter === NULL) {
$groupCenter = new GroupCenter();
$groupCenter
->setCenter($center)
->setPermissionsGroup($pg)
;
$this->em->persist($groupCenter);
}
$this->groupCenters[$center->getId()][$pg->getId()] = $groupCenter;
return $groupCenter;
}
protected function prepareGroupingCenters()
{
$reader = Reader::createFromPath($this->tempInput->getOption('grouping-centers'));
$reader->setHeaderOffset(0);
foreach ($reader->getRecords() as $r) {
$this->centers[$r['alias']] =
$this->centers[$r['alias']] =
\array_merge(
$this->centers[$r['alias']] ?? [],
$this->getCenters($r['center']
@@ -368,18 +313,18 @@ class ChillImportUsersCommand extends Command
);
}
}
/**
* return a list of centers matching the name of alias.
*
*
* If the name match one center, this center is returned in an array.
*
* If the name match an alias, the centers corresponding to the alias are
*
* If the name match an alias, the centers corresponding to the alias are
* returned in an array.
*
*
* If the center is not found or alias is not created, a new center is created
* and suggested to user
*
*
* @param string $name the name of the center or the alias regrouping center
* @return Center[]
*/
@@ -387,62 +332,62 @@ class ChillImportUsersCommand extends Command
{
// sanitize
$name = \trim($name);
if (\array_key_exists($name, $this->centers)) {
return $this->centers[$name];
}
// search for a center with given name
$center = $this->em->getRepository(Center::class)
->findOneByName($name);
if ($center instanceof Center) {
$this->centers[$name] = [$center];
return $this->centers[$name];
}
// suggest and create
$center = (new Center())
->setName($name);
$this->tempOutput->writeln("Center with name \"$name\" not found.");
$qFormatter = $this->getHelper('question');
$question = new ConfirmationQuestion("Create a center with name \"$name\" ?", true);
if ($qFormatter->ask($this->tempInput, $this->tempOutput, $question)) {
$this->centers[$name] = [ $center ];
$errors = $this->validator->validate($center);
if ($errors->count() > 0) {
$errorMessages = $this->concatenateViolations($errors);
$this->tempOutput->writeln(sprintf("%d errors found with center with name \"%s\"", $errors->count(), $name));
$this->tempOutput->writeln($errorMessages);
throw new \RuntimeException("Found errors while creating one center. "
. "Watch messages in command output");
}
$this->em->persist($center);
return $this->centers[$name];
}
return null;
}
protected function concatenateViolations(ConstraintViolationListInterface $list)
{
$str = [];
foreach ($list as $e) {
/* @var $e \Symfony\Component\Validator\ConstraintViolationInterface */
$str[] = $e->getMessage();
}
return \implode(";", $str);
}
}

View File

@@ -1,20 +1,12 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Pagination\PaginatorFactory;
/**
*
*
*/
class AdminCountryCRUDController extends CRUDController
{
function __construct(PaginatorFactory $paginator)
{
$this->paginatorFactory = $paginator;
}
}

View File

@@ -137,17 +137,15 @@ class UserController extends CRUDController
]);
}
/**
*
*
* @param User $user
* @return \Symfony\Component\Form\Form
*/
private function createEditPasswordForm(User $user)
private function createEditPasswordForm(User $user): FormInterface
{
return $this->createForm(UserPasswordType::class, null, array(
'user' => $user
))
return $this->createForm(
UserPasswordType::class,
null,
[
'user' => $user
]
)
->add('submit', SubmitType::class, array('label' => 'Change password'))
->remove('actual_password');
}
@@ -314,7 +312,7 @@ class UserController extends CRUDController
{
$user = $this->getUser();
$form = $this->createForm(UserCurrentLocationType::class, $user)
->add('submit', SubmitType::class, ['label' => 'Change current location'])
->add('submit', SubmitType::class, ['label' => 'Save'])
->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {

View File

@@ -1,19 +1,19 @@
<?php
/*
*
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -23,12 +23,11 @@ namespace Chill\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation as Serializer;
/**
* @ORM\Entity
* @ORM\Table(name="centers")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class Center implements HasCenterInterface
{
@@ -46,9 +45,10 @@ class Center implements HasCenterInterface
* @var string
*
* @ORM\Column(type="string", length=255)
* @Serializer\Groups({"docgen:read"})
*/
private $name;
private string $name = '';
/**
* @var Collection
*
@@ -58,8 +58,8 @@ class Center implements HasCenterInterface
* )
*/
private $groupCenters;
/**
* Center constructor.
*/
@@ -67,7 +67,7 @@ class Center implements HasCenterInterface
{
$this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @return string
*/
@@ -75,7 +75,7 @@ class Center implements HasCenterInterface
{
return $this->name;
}
/**
* @param $name
* @return $this
@@ -85,7 +85,7 @@ class Center implements HasCenterInterface
$this->name = $name;
return $this;
}
/**
* @return int
*/
@@ -93,7 +93,7 @@ class Center implements HasCenterInterface
{
return $this->id;
}
/**
* @return ArrayCollection|Collection
*/
@@ -101,7 +101,7 @@ class Center implements HasCenterInterface
{
return $this->groupCenters;
}
/**
* @param GroupCenter $groupCenter
* @return $this
@@ -111,7 +111,7 @@ class Center implements HasCenterInterface
$this->groupCenters->add($groupCenter);
return $this;
}
/**
* @return string
*/
@@ -119,7 +119,7 @@ class Center implements HasCenterInterface
{
return $this->getName();
}
/**
* @return $this|Center
*/

View File

@@ -1,22 +1,6 @@
<?php
/*
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Entity;
@@ -28,38 +12,30 @@ use Doctrine\Common\Collections\ArrayCollection;
* @ORM\Entity
* @ORM\Table(name="role_scopes")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class RoleScope
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
/**
* @var string
*
* @ORM\Column(type="string", length=255)
*/
private $role;
private ?string $role = null;
/**
* @var Scope
*
* @ORM\ManyToOne(
* targetEntity="Chill\MainBundle\Entity\Scope",
* inversedBy="roleScopes")
* @ORM\JoinColumn(nullable=true, name="scope_id")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/
private $scope;
private ?Scope $scope = null;
/**
* @var Collection
*
@@ -68,59 +44,37 @@ class RoleScope
* mappedBy="roleScopes")
*/
private $permissionsGroups;
/**
* RoleScope constructor.
*/
public function __construct() {
$this->new = true;
$this->permissionsGroups = new ArrayCollection();
}
/**
* @return int
*/
public function getId()
public function getId(): ?int
{
return $this->id;
}
/**
* @return string
*/
public function getRole()
public function getRole(): ?string
{
return $this->role;
}
/**
* @return Scope
*/
public function getScope()
public function getScope(): ?Scope
{
return $this->scope;
}
/**
* @param type $role
* @return RoleScope
*/
public function setRole($role)
public function setRole(?string $role = null): self
{
$this->role = $role;
return $this;
}
/**
* @param Scope $scope
* @return RoleScope
*/
public function setScope(Scope $scope = null)
public function setScope(?Scope $scope = null): self
{
$this->scope = $scope;
return $this;
}
}

View File

@@ -9,7 +9,7 @@ use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Entity\Location;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation as Serializer;
/**
* User
@@ -17,7 +17,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
* @ORM\Entity
* @ORM\Table(name="users")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
* @DiscriminatorMap(typeProperty="type", mapping={
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "user"=User::class
* })
*/
@@ -52,6 +52,7 @@ class User implements AdvancedUserInterface {
/**
* @ORM\Column(type="string", length=200)
* @Serializer\Groups({"docgen:read"})
*/
private string $label = '';
@@ -59,8 +60,9 @@ class User implements AdvancedUserInterface {
* @var string
*
* @ORM\Column(type="string", length=150, nullable=true)
* @Serializer\Groups({"docgen:read"})
*/
private $email;
private ?string $email = null;
/**
* @var string
@@ -124,6 +126,7 @@ class User implements AdvancedUserInterface {
/**
* @var Center|null
* @ORM\ManyToOne(targetEntity=Center::class)
* @Serializer\Groups({"docgen:read"})
*/
private ?Center $mainCenter = null;

View File

@@ -1,50 +1,23 @@
<?php
/*
* Chill is a software for social workers
* Copyright (C) 2016 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Pagination;
/**
* PageGenerator associated with a Paginator
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
* PageGenerator associated with a Paginator.
*/
class PageGenerator implements \Iterator
{
/**
*
* @var Paginator
*/
protected $paginator;
/**
*
* @var int
*/
protected $current = 1;
public function __construct(Paginator $paginator)
protected Paginator $paginator;
protected int $current = 1;
public function __construct(Paginator $paginator)
{
$this->paginator = $paginator;;
}
public function current()
{
return $this->paginator->getPage($current);
@@ -67,7 +40,7 @@ class PageGenerator implements \Iterator
public function valid()
{
return $this->current > 0
return $this->current > 0
&& $this->current <= $this->paginator->countPages();
}
}

View File

@@ -402,3 +402,11 @@ input.belgian_national_number {
&.daily_counter {}
&.control_digit {}
}
// replace abbr
span.item-key {
font-variant: all-small-caps;
font-size: 90%;
background-color: #0000000a;
//text-decoration: dotted underline;
}

View File

@@ -8,3 +8,43 @@ require('./bootstrap.scss');
import Dropdown from 'bootstrap/js/src/dropdown';
import Modal from 'bootstrap/js/dist/modal';
import Collapse from 'bootstrap/js/src/collapse';
import Carousel from 'bootstrap/js/src/carousel';
//
// ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
// Initialize options, and show/hide controls in first/last slides
//
let ACHeaderSlider = document.querySelector('#ACHeaderSlider');
if (ACHeaderSlider) {
let controlPrev = ACHeaderSlider.querySelector('button[data-bs-slide="prev"]'),
controlNext = ACHeaderSlider.querySelector('button[data-bs-slide="next"]'),
length = ACHeaderSlider.querySelectorAll('.carousel-item').length,
last = length-1,
carousel = new Carousel(ACHeaderSlider, {
interval: false,
wrap: false,
ride: false,
keyboard: false,
touch: true
})
;
document.addEventListener('DOMContentLoaded', (e) => {
controlNext.classList.remove('visually-hidden');
});
ACHeaderSlider.addEventListener('slid.bs.carousel', (e) => {
//console.log('from slide', e.direction, e.relatedTarget, e.from, e.to );
switch (e.to) {
case 0:
controlPrev.classList.add('visually-hidden');
controlNext.classList.remove('visually-hidden');
break;
case last:
controlPrev.classList.remove('visually-hidden');
controlNext.classList.add('visually-hidden');
break;
default:
controlPrev.classList.remove('visually-hidden');
controlNext.classList.remove('visually-hidden');
}
})
}

View File

@@ -5,7 +5,7 @@
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ installation.name }} - {% block title %}{% endblock %}</title>
<title>{{ installation.name }} - {% block title %}{{ 'Homepage'|trans }}{% endblock %}</title>
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
{{ encore_entry_link_tags('mod_bootstrap') }}
@@ -68,6 +68,9 @@
<button type="submit" class="btn btn-lg btn-warning mt-3">
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
</button>
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
</a>
</center>
</form>
</div>

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Routing;
use Symfony\Component\Routing\RouteCollection;
@@ -8,43 +10,28 @@ use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
* This class permit to build menu from the routing information
* stored in each bundle.
*
* how to must come here FIXME
*
* how to must come here FIXME
*
* @author julien
*/
class MenuComposer
{
/**
*
* @var RouterInterface
*/
private $router;
/**
*
* @var FactoryInterface
*/
private $menuFactory;
/**
*
* @var TranslatorInterface
*/
private $translator;
/**
*
* @var
*/
private $localMenuBuilders = [];
private RouterInterface $router;
private FactoryInterface $menuFactory;
private TranslatorInterface $translator;
private array $localMenuBuilders = [];
private RouteCollection $routeCollection;
function __construct(
RouterInterface $router,
FactoryInterface $menuFactory,
@@ -60,7 +47,7 @@ class MenuComposer
* This function is needed for testing purpose: routeCollection is not
* available as a service (RouterInterface is provided as a service and
* added to this class as paramater in __construct)
*
*
* @param RouteCollection $routeCollection
*/
public function setRouteCollection(RouteCollection $routeCollection)
@@ -71,7 +58,7 @@ class MenuComposer
/**
* Return an array of routes added to $menuId,
* The array is aimed to build route with MenuTwig
*
*
* @param string $menuId
* @param array $parameters see https://redmine.champs-libres.coop/issues/179
* @return array
@@ -83,7 +70,7 @@ class MenuComposer
foreach ($routeCollection->all() as $routeKey => $route) {
if ($route->hasOption('menus')) {
if (array_key_exists($menuId, $route->getOption('menus'))) {
$route = $route->getOption('menus')[$menuId];
@@ -101,12 +88,12 @@ class MenuComposer
return $routes;
}
public function getMenuFor($menuId, array $parameters = array())
{
$routes = $this->getRoutesFor($menuId, $parameters);
$menu = $this->menuFactory->createItem($menuId);
// build menu from routes
foreach ($routes as $order => $route) {
$menu->addChild($this->translator->trans($route['label']), [
@@ -121,24 +108,24 @@ class MenuComposer
])
;
}
if ($this->hasLocalMenuBuilder($menuId)) {
foreach ($this->localMenuBuilders[$menuId] as $builder) {
/* @var $builder LocalMenuBuilderInterface */
$builder->buildMenu($menuId, $menu, $parameters['args']);
}
}
$this->reorderMenu($menu);
return $menu;
}
/**
* recursive function to resolve the order of a array of routes.
* If the order chosen in routing.yml is already in used, find the
* If the order chosen in routing.yml is already in used, find the
* first next order available.
*
*
* @param array $routes the routes previously added
* @param int $order
* @return int
@@ -151,41 +138,41 @@ class MenuComposer
return $order;
}
}
private function reorderMenu(ItemInterface $menu)
private function reorderMenu(ItemInterface $menu)
{
$ordered = [];
$unordered = [];
foreach ($menu->getChildren() as $name => $item) {
$order = $item->getExtra('order');
if ($order !== null) {
$ordered[$this->resolveOrder($ordered, $order)] = $name;
} else {
$unordered = $name;
}
}
ksort($ordered);
$menus = \array_merge(\array_values($ordered), $unordered);
$menu->reorderChildren($menus);
}
public function addLocalMenuBuilder(LocalMenuBuilderInterface $menuBuilder, $menuId)
public function addLocalMenuBuilder(LocalMenuBuilderInterface $menuBuilder, $menuId)
{
$this->localMenuBuilders[$menuId][] = $menuBuilder;
}
/**
* Return true if the menu has at least one builder.
*
*
* This function is a helper to determine if the method `getMenuFor`
* should be used, or `getRouteFor`. The method `getMenuFor` should be used
* if the result is true (it **does** exists at least one menu builder.
*
*
* @param string $menuId
* @return bool
*/

View File

@@ -26,10 +26,10 @@ use Chill\MainBundle\Search\ParsingException;
/**
* This class implements abstract search with most common responses.
*
*
* you should use this abstract class instead of SearchInterface : if the signature of
* search interface change, the generic method will be implemented here.
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*
*/
@@ -37,7 +37,7 @@ abstract class AbstractSearch implements SearchInterface
{
/**
* parse string expected to be a date and transform to a DateTime object
*
*
* @param type $string
* @return \DateTime
* @throws ParsingException if the date is not parseable
@@ -51,14 +51,14 @@ abstract class AbstractSearch implements SearchInterface
. 'not parsable', 0, $ex);
throw $exception;
}
}
/**
* recompose a pattern, retaining only supported terms
*
*
* the outputted string should be used to show users their search
*
*
* @param array $terms
* @param array $supportedTerms
* @param string $domain if your domain is NULL, you should set NULL. You should set used domain instead
@@ -67,35 +67,35 @@ abstract class AbstractSearch implements SearchInterface
protected function recomposePattern(array $terms, array $supportedTerms, $domain = NULL)
{
$recomposed = '';
if ($domain !== NULL)
{
$recomposed .= '@'.$domain.' ';
}
foreach ($supportedTerms as $term) {
if (array_key_exists($term, $terms) && $term !== '_default') {
$recomposed .= ' '.$term.':';
$containsSpace = \strpos($terms[$term], " ") !== false;
if ($containsSpace) {
$recomposed .= "(";
$recomposed .= '"';
}
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
if ($containsSpace) {
$recomposed .= ")";
$recomposed .= '"';
}
}
}
if ($terms['_default'] !== '') {
$recomposed .= ' '.$terms['_default'];
}
//strip first character if empty
if (mb_strcut($recomposed, 0, 1) === ' '){
$recomposed = mb_strcut($recomposed, 1);
}
return $recomposed;
}
}
}

View File

@@ -20,19 +20,15 @@ class SearchApi
private EntityManagerInterface $em;
private PaginatorFactory $paginator;
private array $providers = [];
private iterable $providers = [];
public function __construct(
EntityManagerInterface $em,
SearchPersonApiProvider $searchPerson,
ThirdPartyApiSearch $thirdPartyApiSearch,
SearchUserApiProvider $searchUser,
iterable $providers,
PaginatorFactory $paginator
) {
$this->em = $em;
$this->providers[] = $searchPerson;
$this->providers[] = $thirdPartyApiSearch;
$this->providers[] = $searchUser;
$this->providers = $providers;
$this->paginator = $paginator;
}
@@ -68,10 +64,15 @@ class SearchApi
private function findProviders(string $pattern, array $types, array $parameters): array
{
return \array_filter(
$this->providers,
fn($p) => $p->supportsTypes($pattern, $types, $parameters)
);
$providers = [];
foreach ($this->providers as $provider) {
if ($provider->supportsTypes($pattern, $types, $parameters)) {
$providers[] = $provider;
}
}
return $providers;
}
private function countItems($providers, $types, $parameters): int
@@ -82,12 +83,12 @@ class SearchApi
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
$countNq->setParameters($parameters);
return $countNq->getSingleScalarResult();
return (int) $countNq->getSingleScalarResult();
}
private function buildCountQuery(array $queries, $types, $parameters)
{
$query = "SELECT COUNT(*) AS count FROM ({union_unordered}) AS sq";
$query = "SELECT SUM(c) AS count FROM ({union_unordered}) AS sq";
$unions = [];
$parameters = [];
@@ -141,17 +142,20 @@ class SearchApi
private function prepareProviders(array $rawResults)
{
$metadatas = [];
$providers = [];
foreach ($rawResults as $r) {
foreach ($this->providers as $k => $p) {
if ($p->supportsResult($r['key'], $r['metadata'])) {
$metadatas[$k][] = $r['metadata'];
$providers[$k] = $p;
break;
}
}
}
foreach ($metadatas as $k => $m) {
$this->providers[$k]->prepare($m);
$providers[$k]->prepare($m);
}
}

View File

@@ -4,6 +4,8 @@ namespace Chill\MainBundle\Search;
class SearchApiQuery
{
private array $select = [];
private array $selectParams = [];
private ?string $selectKey = null;
private array $selectKeyParams = [];
private ?string $jsonbMetadata = null;
@@ -15,6 +17,38 @@ class SearchApiQuery
private array $whereClauses = [];
private array $whereClausesParams = [];
public function addSelectClause(string $select, array $params = []): self
{
$this->select[] = $select;
$this->selectParams = [...$this->selectParams, ...$params];
return $this;
}
public function resetSelectClause(): self
{
$this->select = [];
$this->selectParams = [];
$this->selectKey = null;
$this->selectKeyParams = [];
$this->jsonbMetadata = null;
$this->jsonbMetadataParams = [];
$this->pertinence = null;
$this->pertinenceParams = [];
return $this;
}
public function getSelectClauses(): array
{
return $this->select;
}
public function getSelectParams(): array
{
return $this->selectParams;
}
public function setSelectKey(string $selectKey, array $params = []): self
{
$this->selectKey = $selectKey;
@@ -47,6 +81,16 @@ class SearchApiQuery
return $this;
}
public function getFromClause(): string
{
return $this->fromClause;
}
public function getFromParams(): array
{
return $this->fromClauseParams;
}
/**
* Set the where clause and replace all existing ones.
*
@@ -54,7 +98,7 @@ class SearchApiQuery
public function setWhereClauses(string $whereClause, array $params = []): self
{
$this->whereClauses = [$whereClause];
$this->whereClausesParams = [$params];
$this->whereClausesParams = $params;
return $this;
}
@@ -71,11 +115,53 @@ class SearchApiQuery
public function andWhereClause(string $whereClause, array $params = []): self
{
$this->whereClauses[] = $whereClause;
$this->whereClausesParams[] = $params;
\array_push($this->whereClausesParams, ...$params);
return $this;
}
private function buildSelectParams(bool $count = false): array
{
if ($count) {
return [];
}
$args = $this->getSelectParams();
if (null !== $this->selectKey) {
$args = [...$args, ...$this->selectKeyParams];
}
if (null !== $this->jsonbMetadata) {
$args = [...$args, ...$this->jsonbMetadataParams];
}
if (null !== $this->pertinence) {
$args = [...$args, ...$this->pertinenceParams];
}
return $args;
}
private function buildSelectClause(bool $countOnly = false): string
{
if ($countOnly) {
return 'count(*) AS c';
}
$selects = $this->getSelectClauses();
if (null !== $this->selectKey) {
$selects[] = \strtr("'{key}' AS key", [ '{key}' => $this->selectKey ]);
}
if (null !== $this->jsonbMetadata) {
$selects[] = \strtr('{metadata} AS metadata', [ '{metadata}' => $this->jsonbMetadata]);
}
if (null !== $this->pertinence) {
$selects[] = \strtr('{pertinence} AS pertinence', [ '{pertinence}' => $this->pertinence]);
}
return \implode(', ', $selects);
}
public function buildQuery(bool $countOnly = false): string
{
$isMultiple = count($this->whereClauses);
@@ -87,19 +173,8 @@ class SearchApiQuery
($isMultiple ? ')' : '')
;
if (!$countOnly) {
$select = \strtr("
'{key}' AS key,
{metadata} AS metadata,
{pertinence} AS pertinence
", [
'{key}' => $this->selectKey,
'{metadata}' => $this->jsonbMetadata,
'{pertinence}' => $this->pertinence,
]);
} else {
$select = "1 AS c";
}
$select = $this->buildSelectClause($countOnly);
return \strtr("SELECT
{select}
@@ -116,18 +191,16 @@ class SearchApiQuery
public function buildParameters(bool $countOnly = false): array
{
if (!$countOnly) {
return \array_merge(
$this->selectKeyParams,
$this->jsonbMetadataParams,
$this->pertinenceParams,
$this->fromClauseParams,
\array_merge([], ...$this->whereClausesParams),
);
return [
...$this->buildSelectParams($countOnly),
...$this->fromClauseParams,
...$this->whereClausesParams,
];
} else {
return \array_merge(
$this->fromClauseParams,
\array_merge([], ...$this->whereClausesParams),
);
return [
...$this->fromClauseParams,
...$this->whereClausesParams,
];
}
}
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Search;
use Symfony\Component\Serializer\Annotation as Serializer;
@@ -10,6 +12,8 @@ class SearchApiResult
private $result;
private float $relevance;
public function __construct(float $relevance)
{
$this->relevance = $relevance;
@@ -20,7 +24,7 @@ class SearchApiResult
$this->result = $result;
return $this;
}
}
/**
* @Serializer\Groups({"read"})

View File

@@ -10,10 +10,10 @@ use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
* installed into the app.
* the service is callable from the container with
* $container->get('chill_main.search_provider')
*
* the syntax for search string is :
* - domain, which begin with `@`. Example: `@person`. Restrict the search to some
* entities. It may exists multiple search provider for the same domain (example:
*
* the syntax for search string is :
* - domain, which begin with `@`. Example: `@person`. Restrict the search to some
* entities. It may exists multiple search provider for the same domain (example:
* a search provider for people which performs regular search, and suggestion search
* with phonetical algorithms
* - terms, which are the terms of the search. There are terms with argument (example :
@@ -25,17 +25,17 @@ class SearchProvider
{
/**
*
*
* @var SearchInterface[]
*/
private $searchServices = array();
/**
*
* @var HasAdvancedSearchForm[]
*/
private $hasAdvancedFormSearchServices = array();
/*
* return search services in an array, ordered by
* the order key (defined in service definition)
@@ -59,7 +59,7 @@ class SearchProvider
return $this->searchServices;
}
public function getHasAdvancedFormSearchServices()
{
//sort the array
@@ -75,7 +75,7 @@ class SearchProvider
/**
* parse the search string to extract domain and terms
*
*
* @param string $pattern
* @return string[] an array where the keys are _domain, _default (residual terms) or term
*/
@@ -95,9 +95,9 @@ class SearchProvider
/**
* Extract the domain of the subject
*
*
* The domain begins with `@`. Example: `@person`, `@report`, ....
*
*
* @param type $subject
* @return string
* @throws ParsingException
@@ -121,14 +121,15 @@ class SearchProvider
private function extractTerms(&$subject)
{
$terms = array();
preg_match_all('/([a-z\-]+):([\w\-]+|\([^\(\r\n]+\))/', $subject, $matches);
$matches = [];
preg_match_all('/([a-z\-]+):(([^"][\S\-]+)|"[^"]*")/', $subject, $matches);
foreach ($matches[2] as $key => $match) {
//remove from search pattern
$this->mustBeExtracted[] = $matches[0][$key];
//strip parenthesis
if (mb_substr($match, 0, 1) === '(' &&
mb_substr($match, mb_strlen($match) - 1) === ')') {
if (mb_substr($match, 0, 1) === '"' &&
mb_substr($match, mb_strlen($match) - 1) === '"') {
$match = trim(mb_substr($match, 1, mb_strlen($match) - 2));
}
$terms[$matches[1][$key]] = $match;
@@ -139,14 +140,14 @@ class SearchProvider
/**
* store string which must be extracted to find default arguments
*
*
* @var string[]
*/
private $mustBeExtracted = array();
/**
* extract default (residual) arguments
*
*
* @param string $subject
* @return string
*/
@@ -158,7 +159,7 @@ class SearchProvider
/**
* search through services which supports domain and give
* results as an array of resultsfrom different SearchInterface
*
*
* @param string $pattern
* @param number $start
* @param number $limit
@@ -167,25 +168,25 @@ class SearchProvider
* @return array of results from different SearchInterface
* @throws UnknowSearchDomainException if the domain is unknow
*/
public function getSearchResults($pattern, $start = 0, $limit = 50,
public function getSearchResults($pattern, $start = 0, $limit = 50,
array $options = array(), $format = 'html')
{
$terms = $this->parse($pattern);
$results = array();
//sort searchServices by order
$sortedSearchServices = array();
foreach($this->searchServices as $service) {
$sortedSearchServices[$service->getOrder()] = $service;
}
if ($terms['_domain'] !== NULL) {
foreach ($sortedSearchServices as $service) {
if ($service->supports($terms['_domain'], $format)) {
$results[] = $service->renderResult($terms, $start, $limit, $options);
}
}
if (count($results) === 0) {
throw new UnknowSearchDomainException($terms['_domain']);
}
@@ -196,24 +197,24 @@ class SearchProvider
}
}
}
//sort array
ksort($results);
return $results;
}
public function getResultByName($pattern, $name, $start = 0, $limit = 50,
array $options = array(), $format = 'html')
array $options = array(), $format = 'html')
{
$terms = $this->parse($pattern);
$search = $this->getByName($name);
if ($terms['_domain'] !== NULL && !$search->supports($terms['_domain'], $format))
{
throw new ParsingException("The domain is not supported for the search name");
}
return $search->renderResult($terms, $start, $limit, $options, $format);
}
@@ -232,16 +233,16 @@ class SearchProvider
throw new UnknowSearchNameException($name);
}
}
/**
* return searchservice with an advanced form, defined in service
* return searchservice with an advanced form, defined in service
* definition.
*
*
* @param string $name
* @return HasAdvancedSearchForm
* @throws UnknowSearchNameException
*/
public function getHasAdvancedFormByName($name)
public function getHasAdvancedFormByName($name)
{
if (\array_key_exists($name, $this->hasAdvancedFormSearchServices)) {
return $this->hasAdvancedFormSearchServices[$name];
@@ -253,7 +254,7 @@ class SearchProvider
public function addSearchService(SearchInterface $service, $name)
{
$this->searchServices[$name] = $service;
if ($service instanceof HasAdvancedSearchFormInterface) {
$this->hasAdvancedFormSearchServices[$name] = $service;
}
@@ -477,7 +478,7 @@ class SearchProvider
$string = strtr($string, $chars);
} /* remove from wordpress: we use only utf 8
* else {
// Assume ISO-8859-1 if not UTF-8
$chars['in'] = chr(128) . chr(131) . chr(138) . chr(142) . chr(154) . chr(158)
. chr(159) . chr(162) . chr(165) . chr(181) . chr(192) . chr(193) . chr(194)

View File

@@ -0,0 +1,36 @@
<?php
namespace Chill\MainBundle\Search\Utils;
use \DateTimeImmutable;
class ExtractDateFromPattern
{
private const DATE_PATTERN = [
["([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))", 'Y-m-d'], // 1981-05-12
["((0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([12]\d{3}))", 'd/m/Y'], // 15/12/1980
["((0[1-9]|[12]\d|3[01])-(0[1-9]|1[0-2])-([12]\d{3}))", 'd-m-Y'], // 15/12/1980
];
public function extractDates(string $subject): SearchExtractionResult
{
$dates = [];
$filteredSubject = $subject;
foreach (self::DATE_PATTERN as [$pattern, $format]) {
$matches = [];
\preg_match_all($pattern, $filteredSubject, $matches);
foreach ($matches[0] as $match) {
$date = DateTimeImmutable::createFromFormat($format, $match);
if (false !== $date) {
$dates[] = $date;
// filter string: remove what is found
$filteredSubject = \trim(\strtr($filteredSubject, [$match => ""]));
}
}
}
return new SearchExtractionResult($filteredSubject, $dates);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Chill\MainBundle\Search\Utils;
class ExtractPhonenumberFromPattern
{
private const PATTERN = "([\+]{0,1}[0-9\ ]{5,})";
public function extractPhonenumber(string $subject): SearchExtractionResult
{
$matches = [];
\preg_match(self::PATTERN, $subject,$matches);
if (0 < count($matches)) {
$phonenumber = [];
$length = 0;
foreach (\str_split(\trim($matches[0])) as $key => $char) {
switch ($char) {
case '0':
$length++;
if ($key === 0) { $phonenumber[] = '+32'; }
else { $phonenumber[] = $char; }
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
$length++;
$phonenumber[] = $char;
break;
case ' ':
break;
default:
throw new \LogicException("should not match not alnum character");
}
}
if ($length > 5) {
$filtered = \trim(\strtr($subject, [$matches[0] => '']));
return new SearchExtractionResult($filtered, [\implode('', $phonenumber)] );
}
}
return new SearchExtractionResult($subject, []);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Chill\MainBundle\Search\Utils;
class SearchExtractionResult
{
private string $filteredSubject;
private array $found;
public function __construct(string $filteredSubject, array $found)
{
$this->filteredSubject = $filteredSubject;
$this->found = $found;
}
public function getFound(): array
{
return $this->found;
}
public function hasResult(): bool
{
return [] !== $this->found;
}
public function getFilteredSubject(): string
{
return $this->filteredSubject;
}
}

View File

@@ -1,34 +1,17 @@
<?php
/*
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Security\Authorization;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Voter for Chill software.
*
* This abstract Voter provide generic methods to handle object specific to Chill
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
{
@@ -39,6 +22,8 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
. 'getSupportedAttributes and getSupportedClasses methods.',
E_USER_DEPRECATED);
// @TODO: getSupportedAttributes() should be created in here and made abstract or in ChillVoterInterface.
// @TODO: getSupportedClasses() should be created in here and made abstract or in ChillVoterInterface.
return \in_array($attribute, $this->getSupportedAttributes($attribute))
&& \in_array(\get_class($subject), $this->getSupportedClasses());
}
@@ -49,7 +34,7 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
. 'methods introduced by Symfony 3.0, and do not rely on '
. 'isGranted method', E_USER_DEPRECATED);
// @TODO: isGranted() should be created in here and made abstract or in ChillVoterInterface.
return $this->isGranted($attribute, $subject, $token->getUser());
}
}

View File

@@ -1,48 +1,24 @@
<?php
/*
* Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Security\Authorization;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Security\Core\Role\Role;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ChillExportVoter extends Voter
{
const EXPORT = 'chill_export';
/**
*
* @var AuthorizationHelper
*/
protected $authorizationHelper;
public function __construct(AuthorizationHelper $authorizationHelper)
public const EXPORT = 'chill_export';
protected AuthorizationHelperInterface $authorizationHelper;
public function __construct(AuthorizationHelperInterface $authorizationHelper)
{
$this->authorizationHelper = $authorizationHelper;
}
protected function supports($attribute, $subject): bool
{
return $attribute === self::EXPORT;
@@ -53,10 +29,7 @@ class ChillExportVoter extends Voter
if (!$token->getUser() instanceof User) {
return false;
}
$centers = $this->authorizationHelper
->getReachableCenters($token->getUser(), new Role($attribute));
return count($centers) > 0;
return [] !== $this->authorizationHelper->getReachableCenters($token->getUser(), $attribute);
}
}

View File

@@ -1,27 +1,43 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Security\Resolver;
class CenterResolverDispatcher
use Chill\MainBundle\Entity\Center;
/**
* @deprecated Use CenterResolverManager and its interface CenterResolverManagerInterface
*/
final class CenterResolverDispatcher
{
/**
* @var iterabble|CenterResolverInterface[]
* @var CenterResolverInterface[]
*/
private iterable $resolvers = [];
private iterable $resolvers;
public function __construct(iterable $resolvers)
public function __construct(iterable $resolvers = [])
{
$this->resolvers = $resolvers;
}
/**
* @param mixed $entity
* @param array|null $options
* @param object $entity
* @return null|Center|Center[]
*/
public function resolveCenter($entity, ?array $options = [])
{
foreach($this->resolvers as $priority => $resolver) {
trigger_deprecation(
'ChillMainBundle',
'dev-master',
'
Use the service CenterResolverManager through the interface CenterResolverManagerInterface.
The new method "CenterResolverManagerInterface::resolveCenters(): array" is available and the typing
has been improved in order to avoid mixing types.
'
);
foreach($this->resolvers as $resolver) {
if ($resolver->supports($entity, $options)) {
return $resolver->resolveCenter($entity, $options);
}

View File

@@ -6,12 +6,14 @@ use Chill\MainBundle\Entity\Center;
interface CenterResolverInterface
{
/**
* @param object $entity
*/
public function supports($entity, ?array $options = []): bool;
/**
* @param $entity
* @param array|null $options
* @return Center|array|Center[]
* @param object $entity
* @return Center|Center[]
*/
public function resolveCenter($entity, ?array $options = []);

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Security\Resolver;
use Chill\MainBundle\Entity\Center;
final class CenterResolverManager implements CenterResolverManagerInterface
{
/**
* @var CenterResolverInterface[]
*/
private iterable $resolvers;
public function __construct(iterable $resolvers = [])
{
$this->resolvers = $resolvers;
}
public function resolveCenters($entity, ?array $options = []): array
{
foreach($this->resolvers as $resolver) {
if ($resolver->supports($entity, $options)) {
$resolved = $resolver->resolveCenter($entity, $options);
if (null === $resolved) {
return [];
} elseif ($resolved instanceof Center) {
return [$resolved];
} elseif (\is_array($resolved)) {
return $resolved;
}
throw new \UnexpectedValueException(sprintf("the return type of a %s should be an instance of %s, an array or null. Resolver is %s",
CenterResolverInterface::class, Center::class, get_class($resolver)));
}
}
return [];
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Security\Resolver;
use Chill\MainBundle\Entity\Center;
interface CenterResolverManagerInterface
{
/**
* @param object $entity
* @return Center[]
*/
public function resolveCenters($entity, ?array $options = []): array;
}

View File

@@ -6,20 +6,21 @@ use Twig\TwigFilter;
final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
{
private CenterResolverDispatcher $centerResolverDispatcher;
private CenterResolverManagerInterface $centerResolverDispatcher;
private ScopeResolverDispatcher $scopeResolverDispatcher;
/**
* @param CenterResolverDispatcher $centerResolverDispatcher
*/
public function __construct(CenterResolverDispatcher $centerResolverDispatcher)
public function __construct(CenterResolverManagerInterface $centerResolverDispatcher, ScopeResolverDispatcher $scopeResolverDispatcher)
{
$this->centerResolverDispatcher = $centerResolverDispatcher;
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
}
public function getFilters()
{
return [
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter'])
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter']),
new TwigFilter('chill_resolve_scope', [$this, 'resolveScope']),
new TwigFilter('chill_is_scope_concerned', [$this, 'isScopeConcerned']),
];
}
@@ -30,7 +31,26 @@ final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
*/
public function resolveCenter($entity, ?array $options = [])
{
return $this->centerResolverDispatcher->resolveCenter($entity, $options);
return $this->centerResolverDispatcher->resolveCenters($entity, $options);
}
/**
* @param $entity
* @param array|null $options
* @return bool
*/
public function isScopeConcerned($entity, ?array $options = [])
{
return $this->scopeResolverDispatcher->isConcerned($entity, $options);
}
/**
* @param $entity
* @param array|null $options
* @return array|\Chill\MainBundle\Entity\Scope|\Chill\MainBundle\Entity\Scope[]
*/
public function resolveScope($entity, ?array $options = [])
{
return $this->scopeResolverDispatcher->resolveScope();
}
}

View File

@@ -1,18 +1,18 @@
<?php
/*
*
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -27,7 +27,7 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
*
*
*
*/
class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
@@ -52,7 +52,7 @@ class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof Center;
return $data instanceof Center && $format === 'json';
}
public function denormalize($data, string $type, string $format = null, array $context = [])

View File

@@ -19,23 +19,78 @@
namespace Chill\MainBundle\Serializer\Normalizer;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
class DateNormalizer implements NormalizerInterface, DenormalizerInterface
class DateNormalizer implements ContextAwareNormalizerInterface, DenormalizerInterface
{
private RequestStack $requestStack;
private ParameterBagInterface $parameterBag;
public function __construct(RequestStack $requestStack, ParameterBagInterface $parameterBag)
{
$this->requestStack = $requestStack;
$this->parameterBag = $parameterBag;
}
public function normalize($date, string $format = null, array $context = array())
{
/** @var \DateTimeInterface $date */
return [
'datetime' => $date->format(\DateTimeInterface::ISO8601)
];
switch($format) {
case 'json':
return [
'datetime' => $date->format(\DateTimeInterface::ISO8601)
];
case 'docgen':
if (null === $date) {
return [
'long' => '', 'short' => ''
];
}
$hasTime = $date->format('His') !== "000000";
$request = $this->requestStack->getCurrentRequest();
$locale = null !== $request ? $request->getLocale() : $this->parameterBag->get('kernel.default_locale');
$formatterLong = \IntlDateFormatter::create(
$locale,
\IntlDateFormatter::LONG,
$hasTime ? \IntlDateFormatter::SHORT: \IntlDateFormatter::NONE
);
$formatterShort = \IntlDateFormatter::create(
$locale,
\IntlDateFormatter::SHORT,
$hasTime ? \IntlDateFormatter::SHORT: \IntlDateFormatter::NONE
);
return [
'short' => $formatterShort->format($date),
'long' => $formatterLong->format($date)
];
}
}
public function supportsNormalization($data, string $format = null): bool
public function supportsNormalization($data, string $format = null, array $context = []): bool
{
return $data instanceof \DateTimeInterface;
if ($format === 'json') {
return $data instanceof \DateTimeInterface;
} elseif ($format === 'docgen') {
return $data instanceof \DateTimeInterface || (
$data === null
&& \array_key_exists('docgen:expects', $context)
&& (
$context['docgen:expects'] === \DateTimeInterface::class
|| $context['docgen:expects'] === \DateTime::class
|| $context['docgen:expects'] === \DateTimeImmutable::class
)
);
}
return false;
}
public function denormalize($data, string $type, string $format = null, array $context = [])

View File

@@ -52,6 +52,6 @@ class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof User;
return $format === 'json' && $data instanceof User;
}
}

View File

@@ -1,92 +1,56 @@
<?php
/*
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Templating;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Translation\Translator;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
*
* This helper helps to find the string in current locale from translatable_strings
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*
*/
class TranslatableStringHelper
final class TranslatableStringHelper implements TranslatableStringHelperInterface
{
/**
*
* @var RequestStack
*/
private $requestStack;
private $fallbackLocales;
public function __construct(RequestStack $requestStack, Translator $translator)
private RequestStack $requestStack;
private TranslatorInterface $translator;
public function __construct(RequestStack $requestStack, TranslatorInterface $translator)
{
$this->requestStack = $requestStack;
$this->fallbackLocales = $translator->getFallbackLocales();
$this->translator = $translator;
}
/**
* return the string in current locale if it exists.
*
* If it does not exists; return the name in the first language available.
*
* Return a blank string if any strings are available.
* Return NULL if $translatableString is NULL
*
* @param array $translatableStrings
* @return string
*/
public function localize(array $translatableStrings)
{
if (NULL === $translatableStrings) {
return NULL;
}
$language = $this->requestStack->getCurrentRequest()->getLocale();
if (isset($translatableStrings[$language])) {
return $translatableStrings[$language];
} else {
foreach ($this->fallbackLocales as $locale) {
if (array_key_exists($locale, $translatableStrings)) {
return $translatableStrings[$locale];
}
}
public function localize(array $translatableStrings): ?string
{
if ([] === $translatableStrings) {
return null;
}
$request = $this->requestStack->getCurrentRequest();
if (null === $request) {
return null;
}
$language = $request->getLocale();
if (array_key_exists($language, $translatableStrings)) {
return $translatableStrings[$language];
}
foreach ($this->translator->getFallbackLocales() as $locale) {
if (array_key_exists($locale, $translatableStrings)) {
return $translatableStrings[$locale];
}
}
// no fallback translation... trying the first available
$langs = array_keys($translatableStrings);
if (count($langs) === 0) {
if ([] === $langs) {
return '';
}
return $translatableStrings[$langs[0]];
return $translatableStrings[$langs[0]];
}
}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\Templating;
interface TranslatableStringHelperInterface
{
/**
* Return the string in current locale if it exists.
*
* If it does not exists; return the name in the first language available.
*
* Return a blank string if any strings are available.
*/
public function localize(array $translatableStrings): ?string;
}

View File

@@ -12,7 +12,7 @@ class SearchApiQueryTest extends TestCase
$q = new SearchApiQuery();
$q->setSelectJsonbMetadata('boum')
->setSelectKey('bim')
->setSelectPertinence('1')
->setSelectPertinence('?', ['gamma'])
->setFromClause('badaboum')
->andWhereClause('foo', [ 'alpha' ])
->andWhereClause('bar', [ 'beta' ])
@@ -21,12 +21,12 @@ class SearchApiQueryTest extends TestCase
$query = $q->buildQuery();
$this->assertStringContainsString('(foo) AND (bar)', $query);
$this->assertEquals(['alpha', 'beta'], $q->buildParameters());
$this->assertEquals(['gamma', 'alpha', 'beta'], $q->buildParameters());
$query = $q->buildQuery(true);
$this->assertStringContainsString('(foo) AND (bar)', $query);
$this->assertEquals(['alpha', 'beta'], $q->buildParameters());
$this->assertEquals(['gamma', 'alpha', 'beta'], $q->buildParameters());
}
public function testWithoutWhereClause()
@@ -42,4 +42,20 @@ class SearchApiQueryTest extends TestCase
$this->assertEquals([], $q->buildParameters());
}
public function testBuildParams()
{
$q = new SearchApiQuery();
$q
->addSelectClause('bada', [ 'one', 'two' ])
->addSelectClause('boum', ['three', 'four'])
->setWhereClauses('mywhere', [ 'six', 'seven'])
;
$params = $q->buildParameters();
$this->assertEquals(['six', 'seven'], $q->buildParameters(true));
$this->assertEquals(['one', 'two', 'three', 'four', 'six', 'seven'], $q->buildParameters());
}
}

View File

@@ -26,17 +26,17 @@ use PHPUnit\Framework\TestCase;
class SearchProviderTest extends TestCase
{
/**
*
* @var SearchProvider
* @var SearchProvider
*/
private $search;
public function setUp()
{
$this->search = new SearchProvider();
//add a default service
$this->addSearchService(
$this->createDefaultSearchService('I am default', 10), 'default'
@@ -46,7 +46,7 @@ class SearchProviderTest extends TestCase
$this->createNonDefaultDomainSearchService('I am domain bar', 20, FALSE), 'bar'
);
}
/**
* @expectedException \Chill\MainBundle\Search\UnknowSearchNameException
*/
@@ -54,11 +54,11 @@ class SearchProviderTest extends TestCase
{
$this->search->getByName("invalid name");
}
public function testSimplePattern()
{
$terms = $this->p("@person birthdate:2014-01-02 name:(my name) is not my name");
$terms = $this->p("@person birthdate:2014-01-02 name:\"my name\" is not my name");
$this->assertEquals(array(
'_domain' => 'person',
'birthdate' => '2014-01-02',
@@ -66,40 +66,40 @@ class SearchProviderTest extends TestCase
'name' => 'my name'
), $terms);
}
public function testWithoutDomain()
{
$terms = $this->p('foo:bar residual');
$this->assertEquals(array(
'_domain' => null,
'foo' => 'bar',
'_default' => 'residual'
), $terms);
}
public function testWithoutDefault()
{
$terms = $this->p('@person foo:bar');
$this->assertEquals(array(
'_domain' => 'person',
'foo' => 'bar',
'_default' => ''
), $terms);
}
public function testCapitalLetters()
{
$terms = $this->p('Foo:Bar LOL marCi @PERSON');
$this->assertEquals(array(
'_domain' => 'person',
'_default' => 'lol marci',
'foo' => 'bar'
), $terms);
}
/**
* @expectedException Chill\MainBundle\Search\ParsingException
*/
@@ -107,12 +107,11 @@ class SearchProviderTest extends TestCase
{
$term = $this->p("@person @report");
}
public function testDoubleParenthesis()
{
$terms = $this->p("@papamobile name:(my beautiful name) residual "
. "surname:(i love techno)");
$terms = $this->p('@papamobile name:"my beautiful name" residual surname:"i love techno"');
$this->assertEquals(array(
'_domain' => 'papamobile',
'name' => 'my beautiful name',
@@ -120,65 +119,65 @@ class SearchProviderTest extends TestCase
'surname' => 'i love techno'
), $terms);
}
public function testAccentued()
{
//$this->markTestSkipped('accentued characters must be implemented');
$terms = $this->p('manço bélier aztèque à saloù ê');
$this->assertEquals(array(
'_domain' => NULL,
'_default' => 'manco belier azteque a salou e'
), $terms);
}
public function testAccentuedCapitals()
{
//$this->markTestSkipped('accentued characters must be implemented');
$terms = $this->p('MANÉÀ oÛ lÎ À');
$this->assertEquals(array(
'_domain' => null,
'_default' => 'manea ou li a'
), $terms);
}
public function testTrimInParenthesis()
{
$terms = $this->p('foo:(bar )');
$terms = $this->p('foo:"bar "');
$this->assertEquals(array(
'_domain' => null,
'foo' => 'bar',
'_default' => ''
), $terms);
}
public function testTrimInDefault()
{
$terms = $this->p(' foo bar ');
$this->assertEquals(array(
'_domain' => null,
'_default' => 'foo bar'
), $terms);
}
public function testArgumentNameWithTrait()
{
$terms = $this->p('date-from:2016-05-04');
$this->assertEquals(array(
'_domain' => null,
'date-from' => '2016-05-04',
'_default' => ''
), $terms);
}
/**
* Test the behaviour when no domain is provided in the search pattern :
* Test the behaviour when no domain is provided in the search pattern :
* the default search should be enabled
*/
public function testSearchResultDefault()
@@ -186,12 +185,12 @@ class SearchProviderTest extends TestCase
$response = $this->search->getSearchResults('default search');
//$this->markTestSkipped();
$this->assertEquals(array(
"I am default"
), $response);
), $response);
}
/**
* @expectedException \Chill\MainBundle\Search\UnknowSearchDomainException
*/
@@ -200,49 +199,49 @@ class SearchProviderTest extends TestCase
$response = $this->search->getSearchResults('@unknow domain');
//$this->markTestSkipped();
}
public function testSearchResultDomainSearch()
{
//add a search service which will be supported
$this->addSearchService(
$this->createNonDefaultDomainSearchService("I am domain foo", 100, TRUE), 'foo'
);
$response = $this->search->getSearchResults('@foo default search');
$this->assertEquals(array(
"I am domain foo"
), $response);
}
public function testSearchWithinSpecificSearchName()
{
//add a search service which will be supported
$this->addSearchService(
$this->createNonDefaultDomainSearchService("I am domain foo", 100, TRUE), 'foo'
);
$response = $this->search->getResultByName('@foo search', 'foo');
$this->assertEquals('I am domain foo', $response);
}
/**
* @expectedException \Chill\MainBundle\Search\ParsingException
*/
public function testSearchWithinSpecificSearchNameInConflictWithSupport()
{
$response = $this->search->getResultByName('@foo default search', 'bar');
}
/**
* shortcut for executing parse method
*
*
* @param unknown $pattern
* @return string[]
*/
@@ -250,12 +249,12 @@ class SearchProviderTest extends TestCase
{
return $this->search->parse($pattern);
}
/**
* Add a search service to the chill.main.search_provider
*
*
* Useful for mocking the SearchInterface
*
*
* @param SearchInterface $search
* @param string $name
*/
@@ -264,52 +263,52 @@ class SearchProviderTest extends TestCase
$this->search
->addSearchService($search, $name);
}
private function createDefaultSearchService($result, $order)
{
$mock = $this
->getMockForAbstractClass('Chill\MainBundle\Search\AbstractSearch');
//set the mock as default
$mock->expects($this->any())
->method('isActiveByDefault')
->will($this->returnValue(TRUE));
$mock->expects($this->any())
->method('getOrder')
->will($this->returnValue($order));
//set the return value
$mock->expects($this->any())
->method('renderResult')
->will($this->returnValue($result));
return $mock;
}
private function createNonDefaultDomainSearchService($result, $order, $domain)
{
$mock = $this
->getMockForAbstractClass('Chill\MainBundle\Search\AbstractSearch');
//set the mock as default
$mock->expects($this->any())
->method('isActiveByDefault')
->will($this->returnValue(FALSE));
$mock->expects($this->any())
->method('getOrder')
->will($this->returnValue($order));
$mock->expects($this->any())
->method('supports')
->will($this->returnValue($domain));
//set the return value
$mock->expects($this->any())
->method('renderResult')
->will($this->returnValue($result));
return $mock;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Search\Utils;
use Chill\MainBundle\Search\Utils\ExtractDateFromPattern;
use PHPUnit\Framework\TestCase;
class ExtractDateFromPatternTest extends TestCase
{
/**
* @dataProvider provideSubjects
*/
public function testExtractDate(string $subject, string $filtered, int $count, ...$datesSearched)
{
$extractor = new ExtractDateFromPattern();
$result = $extractor->extractDates($subject);
$this->assertCount($count, $result->getFound());
$this->assertEquals($filtered, $result->getFilteredSubject());
$this->assertContainsOnlyInstancesOf(\DateTimeImmutable::class, $result->getFound());
$dates = \array_map(
function (\DateTimeImmutable $d) {
return $d->format('Y-m-d');
}, $result->getFound()
);
foreach ($datesSearched as $date) {
$this->assertContains($date, $dates);
}
}
public function provideSubjects()
{
yield ["15/06/1981", "", 1, '1981-06-15'];
yield ["15/06/1981 30/12/1987", "", 2, '1981-06-15', '1987-12-30'];
yield ["diallo 15/06/1981", "diallo", 1, '1981-06-15'];
yield ["diallo 31/03/1981", "diallo", 1, '1981-03-31'];
yield ["diallo 15-06-1981", "diallo", 1, '1981-06-15'];
yield ["diallo 1981-12-08", "diallo", 1, '1981-12-08'];
yield ["diallo", "diallo", 0];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Search\Utils;
use Chill\MainBundle\Search\Utils\ExtractPhonenumberFromPattern;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class ExtractPhonenumberFromPatternTest extends KernelTestCase
{
/**
* @dataProvider provideData
*/
public function testExtract($subject, $expectedCount, $expected, $filteredSubject, $msg)
{
$extractor = new ExtractPhonenumberFromPattern();
$result = $extractor->extractPhonenumber($subject);
$this->assertCount($expectedCount, $result->getFound());
$this->assertEquals($filteredSubject, $result->getFilteredSubject());
$this->assertEquals($expected, $result->getFound());
}
public function provideData()
{
yield ['Diallo', 0, [], 'Diallo', "no phonenumber"];
yield ['Diallo 15/06/2021', 0, [], 'Diallo 15/06/2021', "no phonenumber and a date"];
yield ['Diallo 0486 123 456', 1, ['+32486123456'], 'Diallo', "a phonenumber and a name"];
yield ['Diallo 123 456', 1, ['123456'], 'Diallo', "a number and a name, without leadiing 0"];
yield ['123 456', 1, ['123456'], '', "only phonenumber"];
yield ['0123 456', 1, ['+32123456'], '', "only phonenumber with a leading 0"];
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Serializer\Normalizer;
use Chill\MainBundle\Serializer\Normalizer\DateNormalizer;
use Prophecy\Prophet;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
class DateNormalizerTest extends KernelTestCase
{
private Prophet $prophet;
public function setUp()
{
$this->prophet = new Prophet();
}
public function testSupports()
{
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'json'));
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTimeImmutable(), 'json'));
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'docgen'));
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTimeImmutable(), 'docgen'));
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTimeImmutable::class]));
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTimeInterface::class]));
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTime::class]));
$this->assertFalse($this->buildDateNormalizer()->supportsNormalization(new \stdClass(), 'docgen'));
$this->assertFalse($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'xml'));
}
/**
* @dataProvider generateDataNormalize
*/
public function testNormalize($expected, $date, $format, $locale, $msg)
{
$this->assertEquals($expected, $this->buildDateNormalizer($locale)->normalize($date, $format, []), $msg);
}
private function buildDateNormalizer(string $locale = null): DateNormalizer
{
$requestStack = $this->prophet->prophesize(RequestStack::class);
$parameterBag = new ParameterBag();
$parameterBag->set('kernel.default_locale', 'fr');
if ($locale === null) {
$requestStack->getCurrentRequest()->willReturn(null);
} else {
$request = $this->prophet->prophesize(Request::class);
$request->getLocale()->willReturn($locale);
$requestStack->getCurrentRequest()->willReturn($request->reveal());
}
return new DateNormalizer($requestStack->reveal(), $parameterBag);
}
public function generateDataNormalize()
{
$datetime = \DateTime::createFromFormat('Y-m-d H:i:sO', '2021-06-05 15:05:01+02:00');
$date = \DateTime::createFromFormat('Y-m-d H:i:sO', '2021-06-05 00:00:00+02:00');
yield [
['datetime' => '2021-06-05T15:05:01+0200'],
$datetime, 'json', null, 'simple normalization to json'
];
yield [
['long' => '5 juin 2021', 'short' => '05/06/2021'],
$date, 'docgen', 'fr', 'normalization to docgen for a date, with current request'
];
yield [
['long' => '5 juin 2021', 'short' => '05/06/2021'],
$date, 'docgen', null, 'normalization to docgen for a date, without current request'
];
yield [
['long' => '5 juin 2021 à 15:05', 'short' => '05/06/2021 15:05'],
$datetime, 'docgen', null, 'normalization to docgen for a datetime, without current request'
];
yield [
['long' => '', 'short' => ''],
null, 'docgen', null, 'normalization to docgen for a null datetime'
];
}
}

View File

@@ -8,38 +8,29 @@ services:
Chill\MainBundle\Repository\:
resource: '../Repository/'
autowire: true
autoconfigure: true
Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository'
Chill\MainBundle\Serializer\Normalizer\:
resource: '../Serializer/Normalizer'
autoconfigure: true
autowire: true
tags:
- { name: 'serializer.normalizer', priority: 64 }
Chill\MainBundle\Form\Type\:
resource: '../Form/Type'
autoconfigure: true
autowire: true
tags:
- { name: form.type }
Chill\MainBundle\Doctrine\Event\:
resource: '../Doctrine/Event/'
autowire: true
tags:
- { name: 'doctrine.event_subscriber' }
chill.main.helper.translatable_string:
class: Chill\MainBundle\Templating\TranslatableStringHelper
arguments:
- "@request_stack"
- "@translator.default"
Chill\MainBundle\Templating\TranslatableStringHelper: '@chill.main.helper.translatable_string'
Chill\MainBundle\Templating\TranslatableStringHelperInterface: '@Chill\MainBundle\Templating\TranslatableStringHelper'
chill.main.twig.translatable_string:
class: Chill\MainBundle\Templating\TranslatableStringTwig

View File

@@ -1,4 +1,8 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill_main.tag_aware_cache:
class: Symfony\Component\Cache\Adapter\TagAwareAdapter
arguments:

View File

@@ -1,10 +1,9 @@
services:
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\Command\ChillImportUsersCommand:
arguments:
$em: '@Doctrine\ORM\EntityManagerInterface'
$logger: '@Psr\Log\LoggerInterface'
$passwordEncoder: '@Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface'
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
tags:
- { name: console.command }

View File

@@ -1,12 +1,13 @@
services:
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\Controller\:
autowire: true
resource: '../../Controller'
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\PasswordController:
autowire: true
arguments:
$chillLogger: '@monolog.logger.chill'
tags: ['controller.service_arguments']
@@ -28,10 +29,6 @@ services:
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\UserController:
autowire: true
autoconfigure: true
Chill\MainBundle\Controller\NotificationController:
arguments:
$security: '@Symfony\Component\Security\Core\Security'

View File

@@ -1,4 +1,8 @@
services:
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader:
arguments:
$crudConfig: '%chill_main_crud_route_loader_config%'

View File

@@ -1,3 +1,7 @@
---
services:
'Chill\MainBundle\Doctrine\Migrations\VersionComparator': ~
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\Doctrine\Migrations\VersionComparator: ~

View File

@@ -1,9 +1,13 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill.main.export_element_validator:
class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator
tags:
- { name: validator.constraint_validator }
# deprecated in favor of spreadsheet_formatter
# chill.main.export.csv_formatter:
# class: Chill\MainBundle\Export\Formatter\CSVFormatter
@@ -11,7 +15,7 @@ services:
# - "@translator"
# tags:
# - { name: chill.export_formatter, alias: 'csv' }
chill.main.export.spreadsheet_formatter:
class: Chill\MainBundle\Export\Formatter\SpreadSheetFormatter
arguments:
@@ -19,7 +23,7 @@ services:
$exportManager: '@Chill\MainBundle\Export\ExportManager'
tags:
- { name: chill.export_formatter, alias: 'spreadsheet' }
chill.main.export.list_formatter:
class: Chill\MainBundle\Export\Formatter\CSVListFormatter
arguments:
@@ -27,7 +31,7 @@ services:
$exportManager: '@Chill\MainBundle\Export\ExportManager'
tags:
- { name: chill.export_formatter, alias: 'csvlist' }
chill.main.export.list_spreadsheet_formatter:
class: Chill\MainBundle\Export\Formatter\SpreadsheetListFormatter
arguments:
@@ -35,7 +39,7 @@ services:
$exportManager: '@Chill\MainBundle\Export\ExportManager'
tags:
- { name: chill.export_formatter, alias: 'spreadlist' }
chill.main.export.pivoted_list_formatter:
class: Chill\MainBundle\Export\Formatter\CSVPivotedListFormatter
arguments:
@@ -43,4 +47,3 @@ services:
$exportManager: '@Chill\MainBundle\Export\ExportManager'
tags:
- { name: chill.export_formatter, alias: 'csv_pivoted_list' }

View File

@@ -1,4 +1,8 @@
services:
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\DataFixtures\ORM\:
resource: ../../DataFixtures/ORM
tags: [ 'doctrine.fixture.orm' ]

View File

@@ -1,4 +1,7 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill.main.form.type.translatable.string:
class: Chill\MainBundle\Form\Type\TranslatableStringFormType
@@ -39,9 +42,7 @@ services:
tags:
- { name: form.type, alias: select2_chill_language }
Chill\MainBundle\Form\Type\PickCenterType:
autowire: true
autoconfigure: true
Chill\MainBundle\Form\Type\PickCenterType: ~
chill.main.form.type.composed_role_scope:
class: Chill\MainBundle\Form\Type\ComposedRoleScopeType
@@ -62,9 +63,7 @@ services:
tags:
- { name: form.type }
Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader:
autowire: true
autoconfigure: true
Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader: ~
chill.main.form.type.export:
class: Chill\MainBundle\Form\Type\Export\ExportType
@@ -96,14 +95,10 @@ services:
arguments:
- '@Chill\MainBundle\Export\ExportManager'
Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer:
autowire: true
autoconfigure: true
Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer: ~
chill.main.form.advanced_search_type:
class: Chill\MainBundle\Form\AdvancedSearchType
autowire: true
autoconfigure: true
arguments:
- "@chill_main.search_provider"
tags:
@@ -116,9 +111,7 @@ services:
tags:
- { name: form.type }
Chill\MainBundle\Form\UserType:
autowire: true
autoconfigure: true
Chill\MainBundle\Form\UserType: ~
Chill\MainBundle\Form\PermissionsGroupType:
tags:
@@ -131,10 +124,9 @@ services:
tags:
- { name: form.type }
Chill\MainBundle\Form\Type\PickAddressType: ~
Chill\MainBundle\Form\Type\PickAddressType:
autoconfigure: true
autowire: true
Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer: ~
Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer:
autoconfigure: true
@@ -147,3 +139,5 @@ services:
Chill\MainBundle\Form\UserCurrentLocationType:
autowire: true
autoconfigure: true
Chill\MainBundle\Form\Type\LocationFormType: ~

View File

@@ -1,4 +1,8 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill.main.logger:
# a logger to log events from the app (deletion, remove, etc.)
alias: monolog.logger.chill

View File

@@ -1,9 +1,11 @@
services:
Chill\MainBundle\Routing\MenuBuilder\:
resource: '../../Routing/MenuBuilder'
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\Routing\MenuBuilder\:
resource: '../../Routing/MenuBuilder'
Chill\MainBundle\Routing\MenuBuilder\UserMenuBuilder:
arguments:
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'

View File

@@ -1,4 +1,8 @@
services:
_defaults:
autowire: true
autoconfigure: true
Chill\MainBundle\Notification\Mailer:
arguments:
$logger: '@Psr\Log\LoggerInterface'
@@ -9,6 +13,4 @@ services:
$translator: '@Symfony\Component\Translation\TranslatorInterface'
$routeParameters: '%chill_main.notifications%'
Chill\MainBundle\Notification\NotificationRenderer:
autoconfigure: true
autowire: true
Chill\MainBundle\Notification\NotificationRenderer: ~

View File

@@ -1,9 +1,11 @@
services:
_defaults:
autowire: true
autoconfigure: true
chill_main.paginator_factory:
class: Chill\MainBundle\Pagination\PaginatorFactory
public: true
autowire: true
autoconfigure: true
arguments:
- "@request_stack"
- "@router"

Some files were not shown because too many files have changed in this diff Show More