mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'calendar/finalization' into testing
This commit is contained in:
commit
e71bb5d77b
@ -15,6 +15,7 @@ use Chill\CalendarBundle\Entity\Calendar;
|
||||
use Chill\CalendarBundle\Form\CalendarType;
|
||||
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
|
||||
use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface;
|
||||
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
||||
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
@ -147,6 +148,8 @@ class CalendarController extends AbstractController
|
||||
*/
|
||||
public function editAction(Calendar $entity, Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(CalendarVoter::EDIT, $entity);
|
||||
|
||||
if (!$this->remoteCalendarConnector->isReady()) {
|
||||
return $this->remoteCalendarConnector->getMakeReadyResponse($request->getUri());
|
||||
}
|
||||
@ -208,6 +211,8 @@ class CalendarController extends AbstractController
|
||||
*/
|
||||
public function listActionByCourse(AccompanyingPeriod $accompanyingPeriod): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(CalendarVoter::SEE, $accompanyingPeriod);
|
||||
|
||||
$filterOrder = $this->buildListFilterOrder();
|
||||
['from' => $from, 'to' => $to] = $filterOrder->getDateRangeData('startDate');
|
||||
|
||||
@ -228,6 +233,7 @@ class CalendarController extends AbstractController
|
||||
'accompanyingCourse' => $accompanyingPeriod,
|
||||
'paginator' => $paginator,
|
||||
'filterOrder' => $filterOrder,
|
||||
'nbIgnored' => $this->calendarACLAwareRepository->countIgnoredByAccompanyingPeriod($accompanyingPeriod, $from, $to),
|
||||
'hasDocs' => 0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class),
|
||||
]);
|
||||
}
|
||||
@ -239,6 +245,8 @@ class CalendarController extends AbstractController
|
||||
*/
|
||||
public function listActionByPerson(Person $person): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(CalendarVoter::SEE, $person);
|
||||
|
||||
$filterOrder = $this->buildListFilterOrder();
|
||||
['from' => $from, 'to' => $to] = $filterOrder->getDateRangeData('startDate');
|
||||
|
||||
@ -259,6 +267,7 @@ class CalendarController extends AbstractController
|
||||
'person' => $person,
|
||||
'paginator' => $paginator,
|
||||
'filterOrder' => $filterOrder,
|
||||
'nbIgnored' => $this->calendarACLAwareRepository->countIgnoredByPerson($person, $from, $to),
|
||||
'hasDocs' => 0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class),
|
||||
]);
|
||||
}
|
||||
@ -309,7 +318,7 @@ class CalendarController extends AbstractController
|
||||
$view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig';
|
||||
$entity->setAccompanyingPeriod($accompanyingPeriod);
|
||||
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
|
||||
} elseif ($person) {
|
||||
} elseif (null !== $person) {
|
||||
$view = '@ChillCalendar/Calendar/newByPerson.html.twig';
|
||||
$entity->setPerson($person)->addPerson($person);
|
||||
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
|
||||
@ -319,6 +328,8 @@ class CalendarController extends AbstractController
|
||||
$entity->setMainUser($this->userRepository->find($request->query->getInt('mainUser')));
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted(CalendarVoter::CREATE, $entity);
|
||||
|
||||
$form = $this->createForm(CalendarType::class, $entity)
|
||||
->add('save', SubmitType::class);
|
||||
|
||||
@ -442,6 +453,8 @@ class CalendarController extends AbstractController
|
||||
*/
|
||||
public function toActivity(Request $request, Calendar $calendar): RedirectResponse
|
||||
{
|
||||
$this->denyAccessUnlessGranted(CalendarVoter::SEE, $calendar);
|
||||
|
||||
$personsId = array_map(
|
||||
static fn (Person $p): int => $p->getId(),
|
||||
$calendar->getPersons()->toArray()
|
||||
|
@ -12,12 +12,20 @@ declare(strict_types=1);
|
||||
namespace Chill\CalendarBundle\Controller;
|
||||
|
||||
use Chill\CalendarBundle\Entity\Calendar;
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc;
|
||||
use Chill\CalendarBundle\Form\CalendarDocCreateType;
|
||||
use Chill\CalendarBundle\Form\CalendarDocEditType;
|
||||
use Chill\CalendarBundle\Security\Voter\CalendarDocVoter;
|
||||
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
||||
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
@ -32,18 +40,202 @@ class CalendarDocController
|
||||
|
||||
private EngineInterface $engine;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private FormFactoryInterface $formFactory;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private SerializerInterface $serializer;
|
||||
|
||||
private UrlGeneratorInterface $urlGenerator;
|
||||
|
||||
public function __construct(Security $security, DocGeneratorTemplateRepository $docGeneratorTemplateRepository, UrlGeneratorInterface $urlGenerator, EngineInterface $engine)
|
||||
{
|
||||
$this->security = $security;
|
||||
public function __construct(
|
||||
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
|
||||
EngineInterface $engine,
|
||||
EntityManagerInterface $entityManager,
|
||||
FormFactoryInterface $formFactory,
|
||||
Security $security,
|
||||
UrlGeneratorInterface $urlGenerator
|
||||
) {
|
||||
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->engine = $engine;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->formFactory = $formFactory;
|
||||
$this->security = $security;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/calendar/calendar-doc/{id}/new", name="chill_calendar_calendardoc_new")
|
||||
*/
|
||||
public function create(Calendar $calendar, Request $request): Response
|
||||
{
|
||||
$calendarDoc = (new CalendarDoc($calendar, null))->setCalendar($calendar);
|
||||
|
||||
if (!$this->security->isGranted(CalendarDocVoter::EDIT, $calendarDoc)) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
// set variables
|
||||
switch ($calendarDoc->getCalendar()->getContext()) {
|
||||
case 'accompanying_period':
|
||||
$view = '@ChillCalendar/CalendarDoc/new_accompanying_period.html.twig';
|
||||
$returnRoute = 'chill_calendar_calendar_list_by_period';
|
||||
$returnParams = ['id' => $calendarDoc->getCalendar()->getAccompanyingPeriod()->getId()];
|
||||
|
||||
break;
|
||||
|
||||
case 'person':
|
||||
$view = '@ChillCalendar/CalendarDoc/new_person.html.twig';
|
||||
$returnRoute = 'chill_calendar_calendar_list_by_person';
|
||||
$returnParams = ['id' => $calendarDoc->getCalendar()->getPerson()->getId()];
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException('Unsupported context');
|
||||
}
|
||||
|
||||
$calendarDocDTO = new CalendarDoc\CalendarDocCreateDTO();
|
||||
$form = $this->formFactory->create(CalendarDocCreateType::class, $calendarDocDTO);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$calendarDoc->createFromDTO($calendarDocDTO);
|
||||
|
||||
$this->entityManager->persist($calendarDoc);
|
||||
$this->entityManager->flush();
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return new RedirectResponse($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate($returnRoute, $returnParams)
|
||||
);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
$this->engine->render(
|
||||
$view,
|
||||
['calendar_doc' => $calendarDoc, 'form' => $form->createView()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/calendar/calendar-doc/{id}/delete", name="chill_calendar_calendardoc_delete")
|
||||
*/
|
||||
public function delete(CalendarDoc $calendarDoc, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted(CalendarDocVoter::EDIT, $calendarDoc)) {
|
||||
throw new AccessDeniedHttpException('Not authorized to delete document');
|
||||
}
|
||||
|
||||
switch ($calendarDoc->getCalendar()->getContext()) {
|
||||
case 'accompanying_period':
|
||||
$view = '@ChillCalendar/CalendarDoc/delete_accompanying_period.html.twig';
|
||||
$returnRoute = 'chill_calendar_calendar_list_by_period';
|
||||
$returnParams = ['id' => $calendarDoc->getCalendar()->getAccompanyingPeriod()->getId()];
|
||||
|
||||
break;
|
||||
|
||||
case 'person':
|
||||
$view = '@ChillCalendar/CalendarDoc/delete_person.html.twig';
|
||||
$returnRoute = 'chill_calendar_calendar_list_by_person';
|
||||
$returnParams = ['id' => $calendarDoc->getCalendar()->getPerson()->getId()];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$form = $this->formFactory->createBuilder()
|
||||
->add('submit', SubmitType::class, [
|
||||
'label' => 'Delete',
|
||||
])
|
||||
->getForm();
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted()) {
|
||||
$this->entityManager->remove($calendarDoc);
|
||||
$this->entityManager->flush();
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return new RedirectResponse($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate($returnRoute, $returnParams)
|
||||
);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
$this->engine->render(
|
||||
$view,
|
||||
[
|
||||
'calendar_doc' => $calendarDoc,
|
||||
'form' => $form->createView(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/calendar/calendar-doc/{id}/edit", name="chill_calendar_calendardoc_edit")
|
||||
*/
|
||||
public function edit(CalendarDoc $calendarDoc, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted(CalendarDocVoter::EDIT, $calendarDoc)) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
// set variables
|
||||
switch ($calendarDoc->getCalendar()->getContext()) {
|
||||
case 'accompanying_period':
|
||||
$view = '@ChillCalendar/CalendarDoc/edit_accompanying_period.html.twig';
|
||||
$returnRoute = 'chill_calendar_calendar_list_by_period';
|
||||
$returnParams = ['id' => $calendarDoc->getCalendar()->getAccompanyingPeriod()->getId()];
|
||||
|
||||
break;
|
||||
|
||||
case 'person':
|
||||
$view = '@ChillCalendar/CalendarDoc/edit_person.html.twig';
|
||||
$returnRoute = 'chill_calendar_calendar_list_by_person';
|
||||
$returnParams = ['id' => $calendarDoc->getCalendar()->getPerson()->getId()];
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException('Unsupported context');
|
||||
}
|
||||
|
||||
$calendarDocEditDTO = new CalendarDoc\CalendarDocEditDTO($calendarDoc);
|
||||
$form = $this->formFactory->create(CalendarDocEditType::class, $calendarDocEditDTO);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$calendarDoc->editFromDTO($calendarDocEditDTO);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return new RedirectResponse($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate($returnRoute, $returnParams)
|
||||
);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
$this->engine->render(
|
||||
$view,
|
||||
['calendar_doc' => $calendarDoc, 'form' => $form->createView()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\CalendarBundle\DependencyInjection;
|
||||
|
||||
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
@ -52,9 +53,10 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf
|
||||
{
|
||||
$this->preprendRoutes($container);
|
||||
$this->prependCruds($container);
|
||||
$this->prependRoleHierarchy($container);
|
||||
}
|
||||
|
||||
protected function prependCruds(ContainerBuilder $container)
|
||||
private function prependCruds(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('chill_main', [
|
||||
'cruds' => [
|
||||
@ -130,7 +132,18 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf
|
||||
]);
|
||||
}
|
||||
|
||||
protected function preprendRoutes(ContainerBuilder $container)
|
||||
private function prependRoleHierarchy(ContainerBuilder $container): void
|
||||
{
|
||||
$container->prependExtensionConfig('security', [
|
||||
'role_hierarchy' => [
|
||||
CalendarVoter::CREATE => [CalendarVoter::SEE],
|
||||
CalendarVoter::EDIT => [CalendarVoter::SEE],
|
||||
CalendarVoter::DELETE => [CalendarVoter::SEE],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function preprendRoutes(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('chill_main', [
|
||||
'routing' => [
|
||||
|
@ -18,6 +18,7 @@ use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\MainBundle\Entity\Embeddable\PrivateCommentEmbeddable;
|
||||
use Chill\MainBundle\Entity\HasCentersInterface;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
@ -48,7 +49,7 @@ use function in_array;
|
||||
* "chill_calendar_calendar": Calendar::class
|
||||
* })
|
||||
*/
|
||||
class Calendar implements TrackCreationInterface, TrackUpdateInterface
|
||||
class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCentersInterface
|
||||
{
|
||||
use RemoteCalendarTrait;
|
||||
|
||||
@ -312,6 +313,20 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $this->cancelReason;
|
||||
}
|
||||
|
||||
public function getCenters(): ?iterable
|
||||
{
|
||||
switch ($this->getContext()) {
|
||||
case 'person':
|
||||
return [$this->getPerson()->getCenter()];
|
||||
|
||||
case 'accompanying_period':
|
||||
return $this->getAccompanyingPeriod()->getCenters();
|
||||
|
||||
default:
|
||||
throw new LogicException('context not supported: ' . $this->getContext());
|
||||
}
|
||||
}
|
||||
|
||||
public function getComment(): CommentEmbeddable
|
||||
{
|
||||
return $this->comment;
|
||||
|
@ -11,6 +11,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\CalendarBundle\Entity;
|
||||
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc\CalendarDocCreateDTO;
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc\CalendarDocEditDTO;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
@ -52,14 +54,14 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @ORM\ManyToOne(targetEntity=StoredObject::class, cascade={"persist"})
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private StoredObject $storedObject;
|
||||
private ?StoredObject $storedObject;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", nullable=false, options={"default": false})
|
||||
*/
|
||||
private bool $trackDateTimeVersion = false;
|
||||
|
||||
public function __construct(Calendar $calendar, StoredObject $storedObject)
|
||||
public function __construct(Calendar $calendar, ?StoredObject $storedObject)
|
||||
{
|
||||
$this->setCalendar($calendar);
|
||||
|
||||
@ -67,6 +69,22 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
|
||||
$this->datetimeVersion = $calendar->getDateTimeVersion();
|
||||
}
|
||||
|
||||
public function createFromDTO(CalendarDocCreateDTO $calendarDocCreateDTO): void
|
||||
{
|
||||
$this->storedObject = $calendarDocCreateDTO->doc;
|
||||
$this->storedObject->setTitle($calendarDocCreateDTO->title);
|
||||
}
|
||||
|
||||
public function editFromDTO(CalendarDocEditDTO $calendarDocEditDTO): void
|
||||
{
|
||||
if (null !== $calendarDocEditDTO->doc) {
|
||||
$calendarDocEditDTO->doc->setTitle($this->getStoredObject()->getTitle());
|
||||
$this->setStoredObject($calendarDocEditDTO->doc);
|
||||
}
|
||||
|
||||
$this->getStoredObject()->setTitle($calendarDocEditDTO->title);
|
||||
}
|
||||
|
||||
public function getCalendar(): Calendar
|
||||
{
|
||||
return $this->calendar;
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Entity\CalendarDoc;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class CalendarDocCreateDTO
|
||||
{
|
||||
/**
|
||||
* @Assert\NotNull
|
||||
* @Assert\Valid
|
||||
*/
|
||||
public ?StoredObject $doc = null;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
public ?string $title = '';
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Entity\CalendarDoc;
|
||||
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class CalendarDocEditDTO
|
||||
{
|
||||
/**
|
||||
* @Assert\Valid
|
||||
*/
|
||||
public ?StoredObject $doc = null;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
public ?string $title = '';
|
||||
|
||||
public function __construct(CalendarDoc $calendarDoc)
|
||||
{
|
||||
$this->title = $calendarDoc->getStoredObject()->getTitle();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Form;
|
||||
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc\CalendarDocCreateDTO;
|
||||
use Chill\DocStoreBundle\Form\StoredObjectType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CalendarDocCreateType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('title', TextType::class, [
|
||||
'label' => 'chill_calendar.Document title',
|
||||
'required' => true,
|
||||
])
|
||||
->add('doc', StoredObjectType::class, [
|
||||
'label' => 'chill_calendar.Document object',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => CalendarDocCreateDTO::class,
|
||||
]);
|
||||
}
|
||||
}
|
41
src/Bundle/ChillCalendarBundle/Form/CalendarDocEditType.php
Normal file
41
src/Bundle/ChillCalendarBundle/Form/CalendarDocEditType.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Form;
|
||||
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc\CalendarDocEditDTO;
|
||||
use Chill\DocStoreBundle\Form\StoredObjectType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CalendarDocEditType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('title', TextType::class, [
|
||||
'label' => 'chill_calendar.Document title',
|
||||
'required' => true,
|
||||
])
|
||||
->add('doc', StoredObjectType::class, [
|
||||
'label' => 'chill_calendar.Document object',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => CalendarDocEditDTO::class,
|
||||
]);
|
||||
}
|
||||
}
|
@ -64,31 +64,38 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function buildQueryByAccompanyingPeriodIgnoredByDates(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$qb->from(Calendar::class, 'c');
|
||||
|
||||
$andX = $qb->expr()->andX($qb->expr()->eq('c.accompanyingPeriod', ':period'));
|
||||
$qb->setParameter('period', $period);
|
||||
|
||||
if (null !== $startDate) {
|
||||
$andX->add($qb->expr()->lt('c.startDate', ':startDate'));
|
||||
$qb->setParameter('startDate', $startDate);
|
||||
}
|
||||
|
||||
if (null !== $endDate) {
|
||||
$andX->add($qb->expr()->gt('c.endDate', ':endDate'));
|
||||
$qb->setParameter('endDate', $endDate);
|
||||
}
|
||||
|
||||
$qb->where($andX);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base implementation. The list of allowed accompanying period is retrieved "manually" from @see{AccompanyingPeriodACLAwareRepository}.
|
||||
*/
|
||||
public function buildQueryByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
|
||||
{
|
||||
// find the reachable accompanying periods for person
|
||||
$periods = $this->accompanyingPeriodACLAwareRepository->findByPerson($person, AccompanyingPeriodVoter::SEE);
|
||||
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->from(Calendar::class, 'c');
|
||||
|
||||
$qb
|
||||
->where(
|
||||
$qb->expr()->orX(
|
||||
// the calendar where the person is the main person:
|
||||
$qb->expr()->eq('c.person', ':person'),
|
||||
// when the calendar is in a reachable period, and contains person
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->in('c.accompanyingPeriod', ':periods'),
|
||||
$qb->expr()->isMemberOf(':person', 'c.persons')
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter('person', $person)
|
||||
->setParameter('periods', $periods);
|
||||
$this->addQueryByPersonWithoutDate($qb, $person);
|
||||
|
||||
// filter by date
|
||||
if (null !== $startDate) {
|
||||
@ -104,6 +111,30 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
|
||||
return $qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base implementation. The list of allowed accompanying period is retrieved "manually" from @see{AccompanyingPeriodACLAwareRepository}.
|
||||
*/
|
||||
public function buildQueryByPersonIgnoredByDates(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->from(Calendar::class, 'c');
|
||||
|
||||
$this->addQueryByPersonWithoutDate($qb, $person);
|
||||
|
||||
// filter by date
|
||||
if (null !== $startDate) {
|
||||
$qb->andWhere($qb->expr()->lt('c.startDate', ':startDate'))
|
||||
->setParameter('startDate', $startDate);
|
||||
}
|
||||
|
||||
if (null !== $endDate) {
|
||||
$qb->andWhere($qb->expr()->gt('c.endDate', ':endDate'))
|
||||
->setParameter('endDate', $endDate);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
|
||||
{
|
||||
$qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('count(c)');
|
||||
@ -119,6 +150,21 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countIgnoredByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
|
||||
{
|
||||
$qb = $this->buildQueryByAccompanyingPeriodIgnoredByDates($period, $startDate, $endDate)->select('count(c)');
|
||||
|
||||
return $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countIgnoredByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
|
||||
{
|
||||
return $this->buildQueryByPersonIgnoredByDates($person, $startDate, $endDate)
|
||||
->select('COUNT(c)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|Calendar[]
|
||||
*/
|
||||
@ -160,4 +206,25 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
private function addQueryByPersonWithoutDate(QueryBuilder $qb, Person $person): void
|
||||
{
|
||||
// find the reachable accompanying periods for person
|
||||
$periods = $this->accompanyingPeriodACLAwareRepository->findByPerson($person, AccompanyingPeriodVoter::SEE);
|
||||
|
||||
$qb
|
||||
->where(
|
||||
$qb->expr()->orX(
|
||||
// the calendar where the person is the main person:
|
||||
$qb->expr()->eq('c.person', ':person'),
|
||||
// when the calendar is in a reachable period, and contains person
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->in('c.accompanyingPeriod', ':periods'),
|
||||
$qb->expr()->isMemberOf(':person', 'c.persons')
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter('person', $person)
|
||||
->setParameter('periods', $periods);
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,18 @@ interface CalendarACLAwareRepositoryInterface
|
||||
*/
|
||||
public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
|
||||
|
||||
/**
|
||||
* Return the number or calendars associated with an accompanyign period which **does not** match the date conditions.
|
||||
*/
|
||||
public function countIgnoredByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
|
||||
|
||||
/**
|
||||
* Return the number or calendars associated with a person which **does not** match the date conditions.
|
||||
*
|
||||
* See condition on @see{self::findByPerson}.
|
||||
*/
|
||||
public function countIgnoredByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
|
||||
|
||||
/**
|
||||
* @return array|Calendar[]
|
||||
*/
|
||||
|
@ -3,11 +3,6 @@
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
|
||||
|
||||
<style lang="css">
|
||||
|
||||
--bs-btn-padding-y: .25rem; --bs-btn-padding-x: .5rem; --bs-btn-font-size: .75rem;
|
||||
</style>
|
||||
|
||||
<div class="accompanying_course_work-list">
|
||||
<table class="obj-res-eval my-3">
|
||||
<thead>
|
||||
@ -17,27 +12,40 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for d in calendar.documents %}
|
||||
<tr>
|
||||
<td class="eval">
|
||||
<ul class="eval_title">
|
||||
<li>
|
||||
{{ mm.mimeIcon(d.storedObject.type) }}
|
||||
{{ d.storedObject.title }}
|
||||
|
||||
<ul class="record_actions small inline">
|
||||
{% if chill_document_is_editable(d.storedObject) %}
|
||||
<li>
|
||||
{{ d.storedObject|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_CALENDAR_DOC_SEE', d) %}
|
||||
<tr>
|
||||
<td class="eval">
|
||||
<ul class="eval_title">
|
||||
<li>
|
||||
{{ m.download_button(d.storedObject, d.storedObject.title) }}
|
||||
{{ mm.mimeIcon(d.storedObject.type) }}
|
||||
{{ d.storedObject.title }}
|
||||
{% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
|
||||
<span class="badge bg-danger">{{ 'chill_calendar.Document outdated'|trans }}</span>
|
||||
{% endif %}
|
||||
|
||||
<ul class="record_actions small inline">
|
||||
{% if chill_document_is_editable(d.storedObject) and is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendardoc_delete', {'id': d.id})}}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
<li>
|
||||
{{ d.storedObject|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendardoc_edit', {'id': d.id})}}" class="btn btn-edit"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{{ m.download_button(d.storedObject, d.storedObject.title) }}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -112,13 +112,37 @@
|
||||
|
||||
<div class="item-row">
|
||||
<ul class="record_actions">
|
||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', calendar) and hasDocs %}
|
||||
<li>
|
||||
<a class="btn btn-create"
|
||||
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_pick_template', {'id': calendar.id }) }}">
|
||||
{{ 'chill_calendar.Add a document'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', calendar) %}
|
||||
{% if not hasDocs %}
|
||||
<li>
|
||||
<a class="btn btn-create"
|
||||
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_new', {'id': calendar.id }) }}">
|
||||
{{ 'chill_calendar.Add a document'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-create dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ 'chill_calendar.Add a document'|trans }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_pick_template', {'id': calendar.id }) }}">
|
||||
{{ 'chill_calendar.Add a document from template'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_new', {'id': calendar.id }) }}">
|
||||
{{ 'chill_calendar.Upload a document'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if accompanyingCourse is defined and is_granted('CHILL_ACTIVITY_CREATE', accompanyingCourse) and calendar.activity is null %}
|
||||
<li>
|
||||
|
@ -27,9 +27,11 @@
|
||||
|
||||
{% if calendarItems|length == 0 %}
|
||||
<p class="chill-no-data-statement">
|
||||
{{ "There is no calendar items."|trans }}
|
||||
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}"
|
||||
class="btn btn-create button-small"></a>
|
||||
{% if nbIgnored == 0 %}
|
||||
{{ "There is no calendar items."|trans }}
|
||||
{% else %}
|
||||
{{ 'chill_calendar.There are count ignored calendars by date filter'|trans({'nbIgnored': nbIgnored}) }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% else %}
|
||||
{{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'accompanying_course'}) }}
|
||||
|
@ -26,11 +26,11 @@
|
||||
|
||||
{% if calendarItems|length == 0 %}
|
||||
<p class="chill-no-data-statement">
|
||||
{{ "There is no calendar items."|trans }}
|
||||
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'person_id': person.id}) }}"
|
||||
class="btn btn-create btn-sm">
|
||||
{{ 'Create'|trans }}
|
||||
</a>
|
||||
{% if nbIgnored == 0 %}
|
||||
{{ "There is no calendar items."|trans }}
|
||||
{% else %}
|
||||
{{ 'chill_calendar.There are count ignored calendars by date filter'|trans({'nbIgnored': nbIgnored}) }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% else %}
|
||||
{{ include ('@ChillCalendar/Calendar/_list.html.twig', {context: 'person'}) }}
|
||||
|
@ -0,0 +1,19 @@
|
||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
||||
|
||||
{% block title %}{{ 'chill_calendar.Remove a calendar document' |trans }}{% endblock title %}
|
||||
|
||||
{% set accompanyingCourse = calendar_doc.calendar.accompanyingPeriod %}
|
||||
{% set accompanyingCourseId = accompanyingCourse.id %}
|
||||
|
||||
{% block content %}
|
||||
{{ include('@ChillMain/Util/confirmation_template.html.twig',
|
||||
{
|
||||
'title' : 'chill_calendar.Remove a calendar document'|trans,
|
||||
'confirm_question' : 'chill_calendar.Are you sure you want to remove the doc?'|trans,
|
||||
'cancel_route' : 'chill_calendar_calendar_list_by_period',
|
||||
'cancel_parameters' : { 'id' : accompanyingCourse.id },
|
||||
'form' : form
|
||||
} ) }}
|
||||
{% endblock %}
|
@ -0,0 +1,18 @@
|
||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
||||
|
||||
{% block title %}{{ 'chill_calendar.Edit a document' |trans }}{% endblock title %}
|
||||
|
||||
{% set person = calendar_doc.calendar.person %}
|
||||
|
||||
{% block content %}
|
||||
{{ include('@ChillMain/Util/confirmation_template.html.twig',
|
||||
{
|
||||
'title' : 'chill_calendar.Remove a calendar document'|trans,
|
||||
'confirm_question' : 'chill_calendar.Are you sure you want to remove the doc?'|trans,
|
||||
'cancel_route' : 'chill_calendar_calendar_list_by_person',
|
||||
'cancel_parameters' : { 'id' : person.id },
|
||||
'form' : form
|
||||
} ) }}
|
||||
{% endblock %}
|
@ -0,0 +1,40 @@
|
||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
||||
|
||||
{% block title %}{{ 'chill_calendar.Edit a document' |trans }}{% endblock title %}
|
||||
|
||||
{% set accompanyingCourse = calendar_doc.calendar.accompanyingPeriod %}
|
||||
{% set accompanyingCourseId = accompanyingCourse.id %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ 'chill_calendar.Edit a document'|trans }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.doc) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_calendar_calendar_list_by_accompanying_period', {'id': accompanyingCourse.id }) }}">{{ 'Cancel'|trans|chill_return_path_label }}</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
@ -0,0 +1,39 @@
|
||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
||||
|
||||
{% block title %}{{ 'chill_calendar.Edit a document' |trans }}{% endblock title %}
|
||||
|
||||
{% set person = calendar_doc.calendar.person %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ 'chill_calendar.Edit a document'|trans }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.doc) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_calendar_calendar_list_by_person', {'id': person.id }) }}">{{ 'Cancel'|trans|chill_return_path_label }}</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
@ -0,0 +1,40 @@
|
||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
||||
|
||||
{% block title %}{{ 'chill_calendar.Add a document' |trans }}{% endblock title %}
|
||||
|
||||
{% set accompanyingCourse = calendar_doc.calendar.accompanyingPeriod %}
|
||||
{% set accompanyingCourseId = accompanyingCourse.id %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ 'chill_calendar.Add a document'|trans }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.doc) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_calendar_calendar_list_by_accompanying_period', {'id': accompanyingCourse.id }) }}">{{ 'Cancel'|trans|chill_return_path_label }}</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
@ -0,0 +1,39 @@
|
||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
||||
|
||||
{% block title %}{{ 'chill_calendar.Add a document' |trans }}{% endblock title %}
|
||||
|
||||
{% set person = calendar_doc.calendar.person %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ 'chill_calendar.Add a document'|trans }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.doc) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_calendar_calendar_list_by_person', {'id': person.id }) }}">{{ 'Cancel'|trans|chill_return_path_label }}</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Security\Voter;
|
||||
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use UnexpectedValueException;
|
||||
use function in_array;
|
||||
|
||||
class CalendarDocVoter extends Voter
|
||||
{
|
||||
public const EDIT = 'CHILL_CALENDAR_DOC_EDIT';
|
||||
|
||||
public const SEE = 'CHILL_CALENDAR_DOC_SEE';
|
||||
|
||||
private const ALL = [
|
||||
'CHILL_CALENDAR_DOC_EDIT',
|
||||
'CHILL_CALENDAR_DOC_SEE',
|
||||
];
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(Security $security)
|
||||
{
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
protected function supports($attribute, $subject): bool
|
||||
{
|
||||
return in_array($attribute, self::ALL, true) && $subject instanceof CalendarDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CalendarDoc $subject
|
||||
* @param mixed $attribute
|
||||
*/
|
||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
|
||||
{
|
||||
switch ($attribute) {
|
||||
case self::EDIT:
|
||||
return $this->security->isGranted(CalendarVoter::EDIT, $subject->getCalendar());
|
||||
|
||||
case self::SEE:
|
||||
return $this->security->isGranted(CalendarVoter::SEE, $subject->getCalendar());
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException('Attribute not supported: ' . $attribute);
|
||||
}
|
||||
}
|
||||
}
|
@ -20,13 +20,14 @@ namespace Chill\CalendarBundle\Security\Voter;
|
||||
|
||||
use Chill\CalendarBundle\Entity\Calendar;
|
||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use LogicException;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
@ -41,6 +42,10 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
||||
|
||||
public const SEE = 'CHILL_CALENDAR_CALENDAR_SEE';
|
||||
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
private CenterResolverManagerInterface $centerResolverManager;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private VoterHelperInterface $voterHelper;
|
||||
@ -52,8 +57,8 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
||||
$this->security = $security;
|
||||
$this->voterHelper = $voterHelperFactory
|
||||
->generate(self::class)
|
||||
->addCheckFor(AccompanyingPeriod::class, [self::SEE])
|
||||
->addCheckFor(Person::class, [self::SEE])
|
||||
->addCheckFor(AccompanyingPeriod::class, [self::SEE, self::CREATE])
|
||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||
->addCheckFor(Calendar::class, [self::SEE, self::CREATE, self::EDIT, self::DELETE])
|
||||
->build();
|
||||
}
|
||||
@ -93,48 +98,37 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
||||
if ($subject instanceof AccompanyingPeriod) {
|
||||
switch ($attribute) {
|
||||
case self::SEE:
|
||||
case self::CREATE:
|
||||
if ($subject->getStep() === AccompanyingPeriod::STEP_DRAFT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we first check here that the user has read access to the period
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject);
|
||||
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new LogicException('subject not implemented');
|
||||
// There is no scope on Calendar, but there are some on accompanying period
|
||||
// so, to ignore AccompanyingPeriod's scopes, we create a blank Calendar
|
||||
// linked with an accompanying period.
|
||||
return $this->voterHelper->voteOnAttribute($attribute, (new Calendar())->setAccompanyingPeriod($subject), $token);
|
||||
}
|
||||
} elseif ($subject instanceof Person) {
|
||||
switch ($attribute) {
|
||||
case self::SEE:
|
||||
return $this->security->isGranted(PersonVoter::SEE, $subject);
|
||||
|
||||
default:
|
||||
throw new LogicException('subject not implemented');
|
||||
case self::CREATE:
|
||||
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
|
||||
}
|
||||
} elseif ($subject instanceof Calendar) {
|
||||
if (null !== $period = $subject->getAccompanyingPeriod()) {
|
||||
switch ($attribute) {
|
||||
case self::SEE:
|
||||
case self::EDIT:
|
||||
case self::CREATE:
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::SEE, $period);
|
||||
|
||||
case self::DELETE:
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $period);
|
||||
}
|
||||
} elseif (null !== $person = $subject->getPerson()) {
|
||||
switch ($attribute) {
|
||||
case self::SEE:
|
||||
case self::EDIT:
|
||||
case self::CREATE:
|
||||
return $this->security->isGranted(PersonVoter::SEE, $person);
|
||||
|
||||
case self::DELETE:
|
||||
return $this->security->isGranted(PersonVoter::UPDATE, $person);
|
||||
}
|
||||
switch ($attribute) {
|
||||
case self::SEE:
|
||||
case self::EDIT:
|
||||
case self::CREATE:
|
||||
case self::DELETE:
|
||||
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
|
||||
}
|
||||
}
|
||||
|
||||
throw new LogicException('attribute not implemented');
|
||||
throw new LogicException('attribute or not implemented');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CalendarBundle\Tests\Entity;
|
||||
|
||||
use Chill\CalendarBundle\Entity\Calendar;
|
||||
use Chill\CalendarBundle\Entity\CalendarDoc;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class CalendarDocTest extends TestCase
|
||||
{
|
||||
public function testCreateEditFromDTO(): void
|
||||
{
|
||||
$doc = new CalendarDoc(new Calendar(), null);
|
||||
|
||||
$create = new CalendarDoc\CalendarDocCreateDTO();
|
||||
$create->title = 'tagada';
|
||||
$create->doc = $obj1 = new StoredObject();
|
||||
|
||||
$doc->createFromDTO($create);
|
||||
|
||||
$this->assertSame($obj1, $doc->getStoredObject());
|
||||
$this->assertEquals('tagada', $doc->getStoredObject()->getTitle());
|
||||
|
||||
$edit = new CalendarDoc\CalendarDocEditDTO($doc);
|
||||
$edit->title = 'tsointsoin';
|
||||
|
||||
$doc->editFromDTO($edit);
|
||||
|
||||
$this->assertSame($obj1, $doc->getStoredObject());
|
||||
$this->assertEquals('tsointsoin', $doc->getStoredObject()->getTitle());
|
||||
|
||||
$edit2 = new CalendarDoc\CalendarDocEditDTO($doc);
|
||||
$edit2->doc = $obj2 = new StoredObject();
|
||||
|
||||
$doc->editFromDTO($edit2);
|
||||
|
||||
$this->assertSame($obj2, $doc->getStoredObject());
|
||||
$this->assertEquals('tsointsoin', $doc->getStoredObject()->getTitle());
|
||||
|
||||
$edit3 = new CalendarDoc\CalendarDocEditDTO($doc);
|
||||
$edit3->doc = $obj3 = new StoredObject();
|
||||
$edit3->title = 'tagada';
|
||||
|
||||
$doc->editFromDTO($edit3);
|
||||
|
||||
$this->assertSame($obj3, $doc->getStoredObject());
|
||||
$this->assertEquals('tagada', $doc->getStoredObject()->getTitle());
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
chill_calendar:
|
||||
There are count ignored calendars by date filter: >-
|
||||
{nbIgnored, plural,
|
||||
=0 {Il n'y a aucun rendez-vous ignoré par le filtre de date.}
|
||||
one {Il y a un rendez-vous ignoré par le filtre de date. Modifiez le filtre de date pour le voir apparaitre.}
|
||||
few {# rendez-vous sont ignorés par le filtre de date. Modifiez le filtre de date pour les voir apparaitre.}
|
||||
other {# rendez-vous sont ignorés par le filtre de date. Modifiez le filtre de date pour les voir apparaitre.}
|
||||
}
|
@ -55,6 +55,14 @@ chill_calendar:
|
||||
Create and add a document: Créer et ajouter un document
|
||||
Save and add a document: Enregistrer et ajouter un document
|
||||
Create for me: Créer un rendez-vous pour moi-même
|
||||
Edit a document: Modifier un document
|
||||
Document title: Titre
|
||||
Document object: Document
|
||||
Add a document from template: Ajouter un document depuis un gabarit
|
||||
Upload a document: Téléverser un document
|
||||
Remove a calendar document: Supprimer un document d'un rendez-vous
|
||||
Are you sure you want to remove the doc?: Êtes-vous sûr·e de vouloir supprimer le document associé ?
|
||||
Document outdated: La date et l'heure du rendez-vous ont été modifiés après la création du document
|
||||
|
||||
|
||||
remote_ms_graph:
|
||||
|
@ -309,7 +309,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
if ($this->isRoleReached($attribute, $roleScope->getRole())) {
|
||||
//if yes, we have a right on something...
|
||||
// perform check on scope if necessary
|
||||
if ($this->scopeResolverDispatcher->isConcerned($entity)) {
|
||||
if ($this->scopeResolverDispatcher->isConcerned($entity)) {// here, we should also check that the role need a scope
|
||||
$scope = $this->scopeResolverDispatcher->resolveScope($entity);
|
||||
|
||||
if (null === $scope) {
|
||||
|
@ -53,7 +53,7 @@ use Symfony\Component\Validator\GroupSequenceProviderInterface;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function in_array;
|
||||
use function array_key_exists;
|
||||
use const SORT_REGULAR;
|
||||
|
||||
/**
|
||||
@ -644,16 +644,18 @@ class AccompanyingPeriod implements
|
||||
|
||||
public function getCenters(): ?iterable
|
||||
{
|
||||
$centers = [];
|
||||
|
||||
foreach ($this->getPersons() as $person) {
|
||||
if (
|
||||
!in_array($person->getCenter(), $centers ?? [], true)
|
||||
&& null !== $person->getCenter()
|
||||
null !== $person->getCenter()
|
||||
&& !array_key_exists(spl_object_hash($person->getCenter()), $centers)
|
||||
) {
|
||||
$centers[] = $person->getCenter();
|
||||
$centers[spl_object_hash($person->getCenter())] = $person->getCenter();
|
||||
}
|
||||
}
|
||||
|
||||
return $centers ?? null;
|
||||
return array_values($centers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user