Merge remote-tracking branch 'origin/master' into 616_rapid-action

This commit is contained in:
2024-06-12 16:45:43 +02:00
2249 changed files with 42970 additions and 30730 deletions

View File

@@ -21,6 +21,6 @@ class ChillCalendarBundle extends Bundle
{
parent::build($container);
$container->addCompilerPass(new RemoteCalendarCompilerPass());
$container->addCompilerPass(new RemoteCalendarCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
}
}

View File

@@ -29,22 +29,12 @@ use TheNetworg\OAuth2\Client\Provider\Azure;
class AzureGrantAdminConsentAndAcquireToken extends Command
{
private Azure $azure;
private ClientRegistry $clientRegistry;
private MachineTokenStorage $machineTokenStorage;
public function __construct(Azure $azure, ClientRegistry $clientRegistry, MachineTokenStorage $machineTokenStorage)
public function __construct(private readonly Azure $azure, private readonly ClientRegistry $clientRegistry, private readonly MachineTokenStorage $machineTokenStorage)
{
parent::__construct('chill:calendar:msgraph-grant-admin-consent');
$this->azure = $azure;
$this->clientRegistry = $clientRegistry;
$this->machineTokenStorage = $machineTokenStorage;
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
@@ -76,8 +66,8 @@ class AzureGrantAdminConsentAndAcquireToken extends Command
$output->writeln('Token information:');
$output->writeln($token->getToken());
$output->writeln('Expires at: ' . $token->getExpires());
$output->writeln('To inspect the token content, go to https://jwt.ms/#access_token=' . urlencode($token->getToken()));
$output->writeln('Expires at: '.$token->getExpires());
$output->writeln('To inspect the token content, go to https://jwt.ms/#access_token='.urlencode($token->getToken()));
return 0;
}

View File

@@ -21,11 +21,8 @@ namespace Chill\CalendarBundle\Command;
use Chill\CalendarBundle\Exception\UserAbsenceSyncException;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MapCalendarToUser;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSGraphUserRepository;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSUserAbsenceSync;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use DateInterval;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
@@ -48,19 +45,17 @@ final class MapAndSubscribeUserCalendarCommand extends Command
public function execute(InputInterface $input, OutputInterface $output): int
{
$this->logger->info(self::class . ' execute command');
$this->logger->info(self::class.' execute command');
$limit = 50;
$offset = 0;
/** @var DateInterval $interval the interval before the end of the expiration */
$interval = new DateInterval('P1D');
$expiration = (new DateTimeImmutable('now'))->add(new DateInterval($input->getOption('subscription-duration')));
$expiration = (new \DateTimeImmutable('now'))->add(new \DateInterval($input->getOption('subscription-duration')));
$users = $this->userRepository->findAllAsArray('fr');
$created = 0;
$renewed = 0;
$this->logger->info(self::class . ' start user to get - renew', [
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
$this->logger->info(self::class.' start user to get - renew', [
'expiration' => $expiration->format(\DateTimeImmutable::ATOM),
]);
foreach ($users as $u) {
@@ -73,8 +68,8 @@ final class MapAndSubscribeUserCalendarCommand extends Command
$user = $this->userRepository->find($u['id']);
if (null === $user) {
$this->logger->error("could not find user by id", ['uid' => $u['id']]);
$output->writeln("could not find user by id : " . $u['id']);
$this->logger->error('could not find user by id', ['uid' => $u['id']]);
$output->writeln('could not find user by id : '.$u['id']);
continue;
}
@@ -83,8 +78,8 @@ final class MapAndSubscribeUserCalendarCommand extends Command
// if user still does not have userid, continue
if (!$this->mapCalendarToUser->hasUserId($user)) {
$this->logger->warning("user does not have a counterpart on ms api", ['userId' => $user->getId(), 'email' => $user->getEmail()]);
$output->writeln(sprintf("giving up for user with email %s and id %s", $user->getEmail(), $user->getId()));
$this->logger->warning('user does not have a counterpart on ms api', ['userId' => $user->getId(), 'email' => $user->getEmail()]);
$output->writeln(sprintf('giving up for user with email %s and id %s', $user->getEmail(), $user->getId()));
continue;
}
@@ -94,15 +89,14 @@ final class MapAndSubscribeUserCalendarCommand extends Command
try {
$this->userAbsenceSync->syncUserAbsence($user);
} catch (UserAbsenceSyncException $e) {
$this->logger->error("could not sync user absence", ['userId' => $user->getId(), 'email' => $user->getEmail(), 'exception' => $e->getTraceAsString(), "message" => $e->getMessage()]);
$output->writeln(sprintf("Could not sync user absence: id: %s and email: %s", $user->getId(), $user->getEmail()));
throw $e;
$this->logger->error('could not sync user absence', ['userId' => $user->getId(), 'email' => $user->getEmail(), 'exception' => $e->getTraceAsString(), 'message' => $e->getMessage()]);
$output->writeln(sprintf('Could not sync user absence: id: %s and email: %s', $user->getId(), $user->getEmail()));
}
// we first try to renew an existing subscription, if any.
// if not, or if it fails, we try to create a new one
if ($this->mapCalendarToUser->hasActiveSubscription($user)) {
$this->logger->debug(self::class . ' renew a subscription for', [
$this->logger->debug(self::class.' renew a subscription for', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -114,7 +108,7 @@ final class MapAndSubscribeUserCalendarCommand extends Command
if (0 !== $expirationTs) {
++$renewed;
} else {
$this->logger->warning(self::class . ' could not renew subscription for a user', [
$this->logger->warning(self::class.' could not renew subscription for a user', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -122,7 +116,7 @@ final class MapAndSubscribeUserCalendarCommand extends Command
}
if (!$this->mapCalendarToUser->hasActiveSubscription($user)) {
$this->logger->debug(self::class . ' create a subscription for', [
$this->logger->debug(self::class.' create a subscription for', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -134,14 +128,13 @@ final class MapAndSubscribeUserCalendarCommand extends Command
if (0 !== $expirationTs) {
++$created;
} else {
$this->logger->warning(self::class . ' could not create subscription for a user', [
$this->logger->warning(self::class.' could not create subscription for a user', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
}
}
if (0 === $offset % $limit) {
$this->em->flush();
$this->em->clear();
@@ -151,12 +144,12 @@ final class MapAndSubscribeUserCalendarCommand extends Command
$this->em->flush();
$this->em->clear();
$this->logger->warning(self::class . ' process executed', [
$this->logger->warning(self::class.' process executed', [
'created' => $created,
'renewed' => $renewed,
]);
$output->writeln("users synchronized");
$output->writeln('users synchronized');
return 0;
}

View File

@@ -25,13 +25,9 @@ use Symfony\Component\Console\Output\OutputInterface;
class SendShortMessageOnEligibleCalendar extends Command
{
private BulkCalendarShortMessageSender $messageSender;
public function __construct(BulkCalendarShortMessageSender $messageSender)
public function __construct(private readonly BulkCalendarShortMessageSender $messageSender)
{
parent::__construct();
$this->messageSender = $messageSender;
}
public function getName()
@@ -39,7 +35,7 @@ class SendShortMessageOnEligibleCalendar extends Command
return 'chill:calendar:send-short-messages';
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->messageSender->sendBulkMessageToEligibleCalendars();

View File

@@ -26,8 +26,6 @@ use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Service\ShortMessage\ShortMessageTransporterInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use DateInterval;
use DateTimeImmutable;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberType;
@@ -38,39 +36,18 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use UnexpectedValueException;
use function count;
class SendTestShortMessageOnCalendarCommand extends Command
{
private ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder;
private PersonRepository $personRepository;
private PhoneNumberHelperInterface $phoneNumberHelper;
private PhoneNumberUtil $phoneNumberUtil;
private ShortMessageTransporterInterface $transporter;
private UserRepositoryInterface $userRepository;
public function __construct(
PersonRepository $personRepository,
PhoneNumberUtil $phoneNumberUtil,
PhoneNumberHelperInterface $phoneNumberHelper,
ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
ShortMessageTransporterInterface $transporter,
UserRepositoryInterface $userRepository
private readonly PersonRepository $personRepository,
private readonly PhoneNumberUtil $phoneNumberUtil,
private readonly PhoneNumberHelperInterface $phoneNumberHelper,
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
private readonly ShortMessageTransporterInterface $transporter,
private readonly UserRepositoryInterface $userRepository
) {
parent::__construct();
$this->personRepository = $personRepository;
$this->phoneNumberUtil = $phoneNumberUtil;
$this->phoneNumberHelper = $phoneNumberHelper;
$this->messageForCalendarBuilder = $messageForCalendarBuilder;
$this->transporter = $transporter;
$this->userRepository = $userRepository;
}
public function getName()
@@ -93,20 +70,20 @@ class SendTestShortMessageOnCalendarCommand extends Command
// start date
$question = new Question('When will start the appointment ? (default: "1 hour") ', '1 hour');
$startDate = new DateTimeImmutable($helper->ask($input, $output, $question));
$startDate = new \DateTimeImmutable($helper->ask($input, $output, $question));
if (false === $startDate) {
throw new UnexpectedValueException('could not create a date with this date and time');
throw new \UnexpectedValueException('could not create a date with this date and time');
}
$calendar->setStartDate($startDate);
// end date
$question = new Question('How long will last the appointment ? (default: "PT30M") ', 'PT30M');
$interval = new DateInterval($helper->ask($input, $output, $question));
$interval = new \DateInterval($helper->ask($input, $output, $question));
if (false === $interval) {
throw new UnexpectedValueException('could not create the interval');
throw new \UnexpectedValueException('could not create the interval');
}
$calendar->setEndDate($calendar->getStartDate()->add($interval));
@@ -116,17 +93,17 @@ class SendTestShortMessageOnCalendarCommand extends Command
$question
->setValidator(function ($answer): Person {
if (!is_numeric($answer)) {
throw new UnexpectedValueException('the answer must be numeric');
throw new \UnexpectedValueException('the answer must be numeric');
}
if (0 >= (int) $answer) {
throw new UnexpectedValueException('the answer must be greater than zero');
throw new \UnexpectedValueException('the answer must be greater than zero');
}
$person = $this->personRepository->find((int) $answer);
if (null === $person) {
throw new UnexpectedValueException('The person is not found');
throw new \UnexpectedValueException('The person is not found');
}
return $person;
@@ -140,17 +117,17 @@ class SendTestShortMessageOnCalendarCommand extends Command
$question
->setValidator(function ($answer): User {
if (!is_numeric($answer)) {
throw new UnexpectedValueException('the answer must be numeric');
throw new \UnexpectedValueException('the answer must be numeric');
}
if (0 >= (int) $answer) {
throw new UnexpectedValueException('the answer must be greater than zero');
throw new \UnexpectedValueException('the answer must be greater than zero');
}
$user = $this->userRepository->find((int) $answer);
if (null === $user) {
throw new UnexpectedValueException('The user is not found');
throw new \UnexpectedValueException('The user is not found');
}
return $user;
@@ -169,13 +146,13 @@ class SendTestShortMessageOnCalendarCommand extends Command
$question->setNormalizer(function ($answer): PhoneNumber {
if (null === $answer) {
throw new UnexpectedValueException('The person is not found');
throw new \UnexpectedValueException('The person is not found');
}
$phone = $this->phoneNumberUtil->parse($answer, 'BE');
if (!$this->phoneNumberUtil->isPossibleNumberForType($phone, PhoneNumberType::MOBILE)) {
throw new UnexpectedValueException('Phone number si not a mobile');
throw new \UnexpectedValueException('Phone number si not a mobile');
}
return $phone;
@@ -188,7 +165,7 @@ class SendTestShortMessageOnCalendarCommand extends Command
$messages = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar);
if (0 === count($messages)) {
if (0 === \count($messages)) {
$output->writeln('no message to send to this user');
}

View File

@@ -23,6 +23,6 @@ class AdminController extends AbstractController
*/
public function indexAdminAction()
{
return $this->render('ChillCalendarBundle:Admin:index.html.twig');
return $this->render('@ChillCalendar/Admin/index.html.twig');
}
}

View File

@@ -15,7 +15,6 @@ use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Serializer\Model\Collection;
use DateTimeImmutable;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -24,11 +23,8 @@ use Symfony\Component\Routing\Annotation\Route;
class CalendarAPIController extends ApiController
{
private CalendarRepository $calendarRepository;
public function __construct(CalendarRepository $calendarRepository)
public function __construct(private readonly CalendarRepository $calendarRepository)
{
$this->calendarRepository = $calendarRepository;
}
/**
@@ -45,8 +41,8 @@ class CalendarAPIController extends ApiController
throw new BadRequestHttpException('You must provide a dateFrom parameter');
}
if (false === $dateFrom = DateTimeImmutable::createFromFormat(
DateTimeImmutable::ATOM,
if (false === $dateFrom = \DateTimeImmutable::createFromFormat(
\DateTimeImmutable::ATOM,
$request->query->get('dateFrom')
)) {
throw new BadRequestHttpException('dateFrom not parsable');
@@ -56,8 +52,8 @@ class CalendarAPIController extends ApiController
throw new BadRequestHttpException('You must provide a dateTo parameter');
}
if (false === $dateTo = DateTimeImmutable::createFromFormat(
DateTimeImmutable::ATOM,
if (false === $dateTo = \DateTimeImmutable::createFromFormat(
\DateTimeImmutable::ATOM,
$request->query->get('dateTo')
)) {
throw new BadRequestHttpException('dateTo not parsable');

View File

@@ -30,11 +30,8 @@ use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateTimeImmutable;
use Exception;
use http\Exception\UnexpectedValueException;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Form;
@@ -49,56 +46,20 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class CalendarController extends AbstractController
{
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private CalendarACLAwareRepositoryInterface $calendarACLAwareRepository;
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private FilterOrderHelperFactoryInterface $filterOrderHelperFactory;
private LoggerInterface $logger;
private PaginatorFactory $paginator;
private PersonRepository $personRepository;
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
private SerializerInterface $serializer;
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
public function __construct(
CalendarACLAwareRepositoryInterface $calendarACLAwareRepository,
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
LoggerInterface $logger,
PaginatorFactory $paginator,
RemoteCalendarConnectorInterface $remoteCalendarConnector,
SerializerInterface $serializer,
TranslatableStringHelperInterface $translatableStringHelper,
PersonRepository $personRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
UserRepositoryInterface $userRepository,
TranslatorInterface $translator
private readonly CalendarACLAwareRepositoryInterface $calendarACLAwareRepository,
private readonly DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
private readonly LoggerInterface $logger,
private readonly PaginatorFactory $paginator,
private readonly RemoteCalendarConnectorInterface $remoteCalendarConnector,
private readonly SerializerInterface $serializer,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly PersonRepository $personRepository,
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
private readonly UserRepositoryInterface $userRepository,
private readonly TranslatorInterface $translator
) {
$this->calendarACLAwareRepository = $calendarACLAwareRepository;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->filterOrderHelperFactory = $filterOrderHelperFactory;
$this->logger = $logger;
$this->paginator = $paginator;
$this->remoteCalendarConnector = $remoteCalendarConnector;
$this->serializer = $serializer;
$this->translatableStringHelper = $translatableStringHelper;
$this->personRepository = $personRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->userRepository = $userRepository;
$this->translator = $translator;
}
/**
@@ -119,12 +80,12 @@ class CalendarController extends AbstractController
$view = '@ChillCalendar/Calendar/confirm_deleteByPerson.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
} else {
throw new RuntimeException('nor person or accompanying period');
throw new \RuntimeException('nor person or accompanying period');
}
$form = $this->createDeleteForm($entity);
if ($request->getMethod() === Request::METHOD_DELETE) {
if (Request::METHOD_DELETE === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
@@ -175,7 +136,7 @@ class CalendarController extends AbstractController
$view = '@ChillCalendar/Calendar/editByPerson.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
} else {
throw new RuntimeException('no person nor accompanying period');
throw new \RuntimeException('no person nor accompanying period');
}
$form = $this->createForm(CalendarType::class, $entity)
@@ -185,7 +146,7 @@ class CalendarController extends AbstractController
$templates = $this->docGeneratorTemplateRepository->findByEntity(Calendar::class);
foreach ($templates as $template) {
$form->add('save_and_generate_doc_' . $template->getId(), SubmitType::class, [
$form->add('save_and_generate_doc_'.$template->getId(), SubmitType::class, [
'label' => $this->translatableStringHelper->localize($template->getName()),
]);
}
@@ -202,7 +163,7 @@ class CalendarController extends AbstractController
}
foreach ($templates as $template) {
if ($form->get('save_and_generate_doc_' . $template->getId())->isClicked()) {
if ($form->get('save_and_generate_doc_'.$template->getId())->isClicked()) {
return $this->redirectToRoute('chill_docgenerator_generate_from_template', [
'entityClassName' => Calendar::class,
'entityId' => $entity->getId(),
@@ -364,7 +325,7 @@ class CalendarController extends AbstractController
$form->add('save_and_upload_doc', SubmitType::class);
foreach ($templates as $template) {
$form->add('save_and_generate_doc_' . $template->getId(), SubmitType::class, [
$form->add('save_and_generate_doc_'.$template->getId(), SubmitType::class, [
'label' => $this->translatableStringHelper->localize($template->getName()),
]);
}
@@ -379,12 +340,12 @@ class CalendarController extends AbstractController
if ($form->get('save_and_upload_doc')->isClicked()) {
return $this->redirectToRoute('chill_calendar_calendardoc_new', [
'id' => $entity->getId()
'id' => $entity->getId(),
]);
}
foreach ($templates as $template) {
if ($form->get('save_and_generate_doc_' . $template->getId())->isClicked()) {
if ($form->get('save_and_generate_doc_'.$template->getId())->isClicked()) {
return $this->redirectToRoute('chill_docgenerator_generate_from_template', [
'entityClassName' => Calendar::class,
'entityId' => $entity->getId(),
@@ -429,7 +390,7 @@ class CalendarController extends AbstractController
*/
public function showAction(Request $request, int $id): Response
{
throw new Exception('not implemented');
throw new \Exception('not implemented');
$view = null;
$em = $this->getDoctrine()->getManager();
@@ -446,7 +407,7 @@ class CalendarController extends AbstractController
}
/** @var Calendar $entity */
$entity = $em->getRepository(\Chill\CalendarBundle\Entity\Calendar::class)->find($id);
$entity = $em->getRepository(Calendar::class)->find($id);
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Calendar entity.');
@@ -490,7 +451,7 @@ class CalendarController extends AbstractController
'entity' => $entity,
'user' => $user,
'activityData' => $activityData,
//'delete_form' => $deleteForm->createView(),
// 'delete_form' => $deleteForm->createView(),
]);
}
@@ -535,12 +496,12 @@ class CalendarController extends AbstractController
'returnPath' => $request->query->get('returnPath', null),
];
if ($calendar->getContext() === 'accompanying_period') {
if ('accompanying_period' === $calendar->getContext()) {
$routeParams['accompanying_period_id'] = $calendar->getAccompanyingPeriod()->getId();
} elseif ($calendar->getContext() === 'person') {
} elseif ('person' === $calendar->getContext()) {
$routeParams['person_id'] = $calendar->getPerson()->getId();
} else {
throw new RuntimeException('context not found for this calendar');
throw new \RuntimeException('context not found for this calendar');
}
return $this->redirectToRoute('chill_activity_activity_new', $routeParams);
@@ -549,7 +510,7 @@ class CalendarController extends AbstractController
private function buildListFilterOrder(): FilterOrderHelper
{
$filterOrder = $this->filterOrderHelperFactory->create(self::class);
$filterOrder->addDateRange('startDate', null, new DateTimeImmutable('3 days ago'), null);
$filterOrder->addDateRange('startDate', null, new \DateTimeImmutable('3 days ago'), null);
return $filterOrder->build();
}

View File

@@ -16,7 +16,6 @@ use Chill\CalendarBundle\Entity\CalendarDoc;
use Chill\CalendarBundle\Form\CalendarDocCreateType;
use Chill\CalendarBundle\Form\CalendarDocEditType;
use Chill\CalendarBundle\Security\Voter\CalendarDocVoter;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormFactoryInterface;
@@ -27,40 +26,16 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Templating\EngineInterface;
use UnexpectedValueException;
class CalendarDocController
final readonly class CalendarDocController
{
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private EngineInterface $engine;
private EntityManagerInterface $entityManager;
private FormFactoryInterface $formFactory;
private Security $security;
private SerializerInterface $serializer;
private UrlGeneratorInterface $urlGenerator;
public function __construct(
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
EngineInterface $engine,
EntityManagerInterface $entityManager,
FormFactoryInterface $formFactory,
Security $security,
UrlGeneratorInterface $urlGenerator
private \Twig\Environment $engine,
private EntityManagerInterface $entityManager,
private FormFactoryInterface $formFactory,
private Security $security,
private UrlGeneratorInterface $urlGenerator,
) {
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->engine = $engine;
$this->entityManager = $entityManager;
$this->formFactory = $formFactory;
$this->security = $security;
$this->urlGenerator = $urlGenerator;
}
/**
@@ -91,7 +66,7 @@ class CalendarDocController
break;
default:
throw new UnexpectedValueException('Unsupported context');
throw new \UnexpectedValueException('Unsupported context');
}
$calendarDocDTO = new CalendarDoc\CalendarDocCreateDTO();
@@ -208,7 +183,7 @@ class CalendarDocController
break;
default:
throw new UnexpectedValueException('Unsupported context');
throw new \UnexpectedValueException('Unsupported context');
}
$calendarDocEditDTO = new CalendarDoc\CalendarDocEditDTO($calendarDoc);

View File

@@ -15,21 +15,16 @@ use Chill\CalendarBundle\Repository\CalendarRangeRepository;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Serializer\Model\Collection;
use DateTimeImmutable;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
class CalendarRangeAPIController extends ApiController
{
private CalendarRangeRepository $calendarRangeRepository;
public function __construct(CalendarRangeRepository $calendarRangeRepository)
public function __construct(private readonly CalendarRangeRepository $calendarRangeRepository)
{
$this->calendarRangeRepository = $calendarRangeRepository;
}
/**
@@ -40,15 +35,15 @@ class CalendarRangeAPIController extends ApiController
*/
public function availableRanges(User $user, Request $request, string $_format): JsonResponse
{
//return new JsonResponse(['ok' => true], 200, [], false);
// return new JsonResponse(['ok' => true], 200, [], false);
$this->denyAccessUnlessGranted('ROLE_USER');
if (!$request->query->has('dateFrom')) {
throw new BadRequestHttpException('You must provide a dateFrom parameter');
}
if (false === $dateFrom = DateTimeImmutable::createFromFormat(
DateTimeImmutable::ATOM,
if (false === $dateFrom = \DateTimeImmutable::createFromFormat(
\DateTimeImmutable::ATOM,
$request->query->get('dateFrom')
)) {
throw new BadRequestHttpException('dateFrom not parsable');
@@ -58,8 +53,8 @@ class CalendarRangeAPIController extends ApiController
throw new BadRequestHttpException('You must provide a dateTo parameter');
}
if (false === $dateTo = DateTimeImmutable::createFromFormat(
DateTimeImmutable::ATOM,
if (false === $dateTo = \DateTimeImmutable::createFromFormat(
\DateTimeImmutable::ATOM,
$request->query->get('dateTo')
)) {
throw new BadRequestHttpException('dateTo not parsable');

View File

@@ -31,21 +31,11 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use function in_array;
class InviteApiController
{
private EntityManagerInterface $entityManager;
private MessageBusInterface $messageBus;
private Security $security;
public function __construct(EntityManagerInterface $entityManager, MessageBusInterface $messageBus, Security $security)
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly MessageBusInterface $messageBus, private readonly Security $security)
{
$this->entityManager = $entityManager;
$this->messageBus = $messageBus;
$this->security = $security;
}
/**
@@ -69,7 +59,7 @@ class InviteApiController
throw new AccessDeniedHttpException('not allowed to answer on this invitation');
}
if (!in_array($answer, Invite::STATUSES, true)) {
if (!\in_array($answer, Invite::STATUSES, true)) {
throw new BadRequestHttpException('answer not valid');
}

View File

@@ -30,16 +30,8 @@ use TheNetworg\OAuth2\Client\Token\AccessToken;
class RemoteCalendarConnectAzureController
{
private ClientRegistry $clientRegistry;
private OnBehalfOfUserTokenStorage $MSGraphTokenStorage;
public function __construct(
ClientRegistry $clientRegistry,
OnBehalfOfUserTokenStorage $MSGraphTokenStorage
) {
$this->clientRegistry = $clientRegistry;
$this->MSGraphTokenStorage = $MSGraphTokenStorage;
public function __construct(private readonly ClientRegistry $clientRegistry, private readonly OnBehalfOfUserTokenStorage $MSGraphTokenStorage)
{
}
/**

View File

@@ -19,21 +19,16 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage;
use JsonException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
use const JSON_THROW_ON_ERROR;
class RemoteCalendarMSGraphSyncController
{
private MessageBusInterface $messageBus;
public function __construct(MessageBusInterface $messageBus)
public function __construct(private readonly MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
}
/**
@@ -49,8 +44,8 @@ class RemoteCalendarMSGraphSyncController
}
try {
$body = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
$body = json_decode($request->getContent(), true, 512, \JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new BadRequestHttpException('could not decode json', $e);
}

View File

@@ -22,31 +22,20 @@ use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterfa
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Serializer\Model\Collection;
use DateTimeImmutable;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use function count;
/**
* Contains method to get events (Calendar) from remote calendar.
*/
class RemoteCalendarProxyController
{
private PaginatorFactory $paginatorFactory;
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
private SerializerInterface $serializer;
public function __construct(PaginatorFactory $paginatorFactory, RemoteCalendarConnectorInterface $remoteCalendarConnector, SerializerInterface $serializer)
public function __construct(private readonly PaginatorFactory $paginatorFactory, private readonly RemoteCalendarConnectorInterface $remoteCalendarConnector, private readonly SerializerInterface $serializer)
{
$this->paginatorFactory = $paginatorFactory;
$this->remoteCalendarConnector = $remoteCalendarConnector;
$this->serializer = $serializer;
}
/**
@@ -58,8 +47,8 @@ class RemoteCalendarProxyController
throw new BadRequestHttpException('You must provide a dateFrom parameter');
}
if (false === $dateFrom = DateTimeImmutable::createFromFormat(
DateTimeImmutable::ATOM,
if (false === $dateFrom = \DateTimeImmutable::createFromFormat(
\DateTimeImmutable::ATOM,
$request->query->get('dateFrom')
)) {
throw new BadRequestHttpException('dateFrom not parsable');
@@ -69,8 +58,8 @@ class RemoteCalendarProxyController
throw new BadRequestHttpException('You must provide a dateTo parameter');
}
if (false === $dateTo = DateTimeImmutable::createFromFormat(
DateTimeImmutable::ATOM,
if (false === $dateTo = \DateTimeImmutable::createFromFormat(
\DateTimeImmutable::ATOM,
$request->query->get('dateTo')
)) {
throw new BadRequestHttpException('dateTo not parsable');
@@ -98,8 +87,8 @@ class RemoteCalendarProxyController
// in some case, we cannot paginate: we have to fetch all the items at once. We must avoid
// further requests by forcing the number of items returned.
if (count($events) > $paginator->getItemsPerPage()) {
$paginator->setItemsPerPage(count($events));
if (\count($events) > $paginator->getItemsPerPage()) {
$paginator->setItemsPerPage(\count($events));
}
$collection = new Collection($events, $paginator);

View File

@@ -0,0 +1,54 @@
<?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\DataFixtures\ORM;
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
use Chill\MainBundle\Entity\PermissionsGroup;
use Chill\MainBundle\Entity\RoleScope;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class LoadCalendarACL extends Fixture implements OrderedFixtureInterface
{
public function load(ObjectManager $manager): void
{
$roleScopes = [];
foreach ([
CalendarVoter::CREATE,
CalendarVoter::DELETE,
CalendarVoter::DELETE,
] as $role) {
$roleScopes[] = $r = (new RoleScope())
->setRole($role);
$manager->persist($r);
}
foreach (LoadPermissionsGroup::$refs as $permissionGroupRef) {
/** @var PermissionsGroup $group */
$group = $this->getReference($permissionGroupRef);
foreach ($roleScopes as $scope) {
$group->addRoleScope($scope);
}
}
$manager->flush();
}
public function getOrder(): int
{
return 16000;
}
}

View File

@@ -18,7 +18,6 @@ use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Entity\PostalCode;
use Chill\MainBundle\Repository\UserRepository;
use DateTimeImmutable;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
@@ -29,12 +28,8 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
{
public static array $references = [];
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository
) {
$this->userRepository = $userRepository;
public function __construct(private readonly UserRepository $userRepository)
{
}
public static function getGroups(): array
@@ -49,10 +44,6 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
public function load(ObjectManager $manager): void
{
$arr = range(-50, 50);
echo "Creating calendar range ('plage de disponibilités')\n";
$users = $this->userRepository->findAll();
$location = (new Location())
@@ -73,6 +64,8 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
$manager->persist($type);
$manager->persist($location);
$now = new \DateTimeImmutable();
$days = [
'2021-08-23',
'2021-08-24',
@@ -82,8 +75,12 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
'2021-08-31',
'2021-09-01',
'2021-09-02',
(new DateTimeImmutable('tomorrow'))->format('Y-m-d'),
(new DateTimeImmutable('today'))->format('Y-m-d'),
(new \DateTimeImmutable('tomorrow'))->format('Y-m-d'),
(new \DateTimeImmutable('today'))->format('Y-m-d'),
$now->add(new \DateInterval('P7D'))->format('Y-m-d'),
$now->add(new \DateInterval('P8D'))->format('Y-m-d'),
$now->add(new \DateInterval('P9D'))->format('Y-m-d'),
$now->add(new \DateInterval('P10D'))->format('Y-m-d'),
];
$hours = [
@@ -96,9 +93,9 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
foreach ($users as $u) {
foreach ($days as $d) {
foreach ($hours as $h) {
$event = $d . ' ' . $h;
$startEvent = new DateTimeImmutable($event);
$endEvent = new DateTimeImmutable($event . ' + 1 hours');
$event = $d.' '.$h;
$startEvent = new \DateTimeImmutable($event);
$endEvent = new \DateTimeImmutable($event.' + 1 hours');
$calendarRange = (new CalendarRange())
->setUser($u)
->setStartDate($startEvent)

View File

@@ -39,12 +39,12 @@ class LoadCancelReason extends Fixture implements FixtureGroupInterface
];
foreach ($arr as $a) {
echo 'Creating calendar cancel reason : ' . $a['name'] . "\n";
echo 'Creating calendar cancel reason : '.$a['name']."\n";
$cancelReason = (new CancelReason())
->setCanceledBy($a['name'])
->setActive(true);
$manager->persist($cancelReason);
$reference = 'CancelReason_' . $a['name'];
$reference = 'CancelReason_'.$a['name'];
$this->addReference($reference, $cancelReason);
static::$references[] = $reference;
}

View File

@@ -46,12 +46,12 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
];
foreach ($arr as $a) {
echo 'Creating calendar invite : ' . $a['name']['fr'] . "\n";
echo 'Creating calendar invite : '.$a['name']['fr']."\n";
$invite = (new Invite())
->setStatus($a['status'])
->setUser($this->getRandomUser());
$manager->persist($invite);
$reference = 'Invite_' . $a['name']['fr'];
$reference = 'Invite_'.$a['name']['fr'];
$this->addReference($reference, $invite);
static::$references[] = $reference;
}

View File

@@ -31,7 +31,7 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$loader->load('services/exports.yaml');
$loader->load('services/controller.yml');

View File

@@ -24,28 +24,26 @@ use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateInterval;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping as ORM;
use LogicException;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use function in_array;
/**
* @ORM\Table(
* name="chill_calendar.calendar",
* uniqueConstraints={@ORM\UniqueConstraint(name="idx_calendar_remote", columns={"remoteId"}, options={"where": "remoteId <> ''"})}
* )
*
* @ORM\Entity
*
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "chill_calendar_calendar": Calendar::class
* })
@@ -58,20 +56,20 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
use TrackUpdateTrait;
public const SMS_CANCEL_PENDING = 'sms_cancel_pending';
final public const SMS_CANCEL_PENDING = 'sms_cancel_pending';
public const SMS_PENDING = 'sms_pending';
final public const SMS_PENDING = 'sms_pending';
public const SMS_SENT = 'sms_sent';
final public const SMS_SENT = 'sms_sent';
public const STATUS_CANCELED = 'canceled';
final public const STATUS_CANCELED = 'canceled';
/**
* @deprecated
*/
public const STATUS_MOVED = 'moved';
final public const STATUS_MOVED = 'moved';
public const STATUS_VALID = 'valid';
final public const STATUS_VALID = 'valid';
/**
* a list of invite which have been added during this session.
@@ -93,6 +91,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars")
*
* @Serializer\Groups({"calendar:read", "read"})
*/
private ?AccompanyingPeriod $accompanyingPeriod = null;
@@ -104,6 +103,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @ORM\OneToOne(targetEntity="CalendarRange", inversedBy="calendar")
*
* @Serializer\Groups({"calendar:read", "read"})
*/
private ?CalendarRange $calendarRange = null;
@@ -115,6 +115,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_")
*
* @Serializer\Groups({"calendar:read", "read", "docgen:read"})
*/
private CommentEmbeddable $comment;
@@ -125,22 +126,28 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
private int $dateTimeVersion = 0;
/**
* @var Collection<CalendarDoc::class>
* @var Collection<CalendarDoc>
*
* @ORM\OneToMany(targetEntity=CalendarDoc::class, mappedBy="calendar", orphanRemoval=true)
*/
private Collection $documents;
/**
* @ORM\Column(type="datetime_immutable", nullable=false)
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*
* @Assert\NotNull(message="calendar.An end date is required")
*/
private ?DateTimeImmutable $endDate = null;
private ?\DateTimeImmutable $endDate = null;
/**
* @ORM\Id
*
* @ORM\GeneratedValue
*
* @ORM\Column(type="integer")
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*/
private ?int $id = null;
@@ -152,57 +159,80 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
* orphanRemoval=true,
* cascade={"persist", "remove", "merge", "detach"}
* )
*
* @ORM\JoinTable(name="chill_calendar.calendar_to_invites")
*
* @Serializer\Groups({"read", "docgen:read"})
*
* @var Collection&Selectable<int, Invite>
*/
private Collection $invites;
private Collection&Selectable $invites;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
*
* @Serializer\Groups({"read", "docgen:read"})
*
* @Assert\NotNull(message="calendar.A location is required")
*/
private ?Location $location = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*
* @Assert\NotNull(message="calendar.A main user is mandatory")
*/
private ?User $mainUser = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
*
* @ORM\JoinColumn(nullable=true)
*/
private ?Person $person = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person", inversedBy="calendars")
*
* @ORM\JoinTable(name="chill_calendar.calendar_to_persons")
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*
* @Assert\Count(min=1, minMessage="calendar.At least {{ limit }} person is required.")
*
* @var Collection<Person>
*/
private Collection $persons;
/**
* @ORM\Embedded(class=PrivateCommentEmbeddable::class, columnPrefix="privateComment_")
*
* @Serializer\Groups({"calendar:read"})
*/
private PrivateCommentEmbeddable $privateComment;
/**
* @var Collection<ThirdParty>
*
* @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
*
* @ORM\JoinTable(name="chill_calendar.calendar_to_thirdparties")
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*/
private Collection $professionals;
/**
* @ORM\Column(type="boolean", nullable=true)
*
* @Serializer\Groups({"docgen:read"})
*/
private ?bool $sendSMS = false;
@@ -214,21 +244,27 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @ORM\Column(type="datetime_immutable", nullable=false)
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*
* @Assert\NotNull(message="calendar.A start date is required")
*/
private ?DateTimeImmutable $startDate = null;
private ?\DateTimeImmutable $startDate = null;
/**
* @ORM\Column(type="string", length=255, nullable=false, options={"default": "valid"})
*
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
*
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*/
private string $status = self::STATUS_VALID;
/**
* @ORM\Column(type="boolean", nullable=true)
*
* @Serializer\Groups({"docgen:read"})
*/
private ?bool $urgent = false;
@@ -261,7 +297,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
public function addInvite(Invite $invite): self
{
if ($invite->getCalendar() instanceof Calendar && $invite->getCalendar() !== $this) {
throw new LogicException('Not allowed to move an invitation to another Calendar');
throw new \LogicException('Not allowed to move an invitation to another Calendar');
}
$this->invites[] = $invite;
@@ -317,16 +353,11 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
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());
}
return match ($this->getContext()) {
'person' => [$this->getPerson()->getCenter()],
'accompanying_period' => $this->getAccompanyingPeriod()->getCenters(),
default => throw new \LogicException('context not supported: '.$this->getContext()),
};
}
public function getComment(): CommentEmbeddable
@@ -339,11 +370,11 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
*/
public function getContext(): ?string
{
if ($this->getAccompanyingPeriod() !== null) {
if (null !== $this->getAccompanyingPeriod()) {
return 'accompanying_period';
}
if ($this->getPerson() !== null) {
if (null !== $this->getPerson()) {
return 'person';
}
@@ -366,16 +397,16 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @Serializer\Groups({"docgen:read"})
*/
public function getDuration(): ?DateInterval
public function getDuration(): ?\DateInterval
{
if ($this->getStartDate() === null || $this->getEndDate() === null) {
if (null === $this->getStartDate() || null === $this->getEndDate()) {
return null;
}
return $this->getStartDate()->diff($this->getEndDate());
}
public function getEndDate(): ?DateTimeImmutable
public function getEndDate(): ?\DateTimeImmutable
{
return $this->endDate;
}
@@ -454,7 +485,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
$personsNotAssociated = [];
foreach ($this->persons as $person) {
if (!in_array($person, $this->getPersonsAssociated(), true)) {
if (!\in_array($person, $this->getPersonsAssociated(), true)) {
$personsNotAssociated[] = $person;
}
}
@@ -488,7 +519,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
return $this->smsStatus;
}
public function getStartDate(): ?DateTimeImmutable
public function getStartDate(): ?\DateTimeImmutable
{
return $this->startDate;
}
@@ -510,6 +541,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @return ReadableCollection<(int|string), User>
*
* @Serializer\Groups({"calendar:read", "read"})
*/
public function getUsers(): ReadableCollection
@@ -559,7 +591,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
public function removeDocument(CalendarDoc $calendarDoc): self
{
if ($calendarDoc->getCalendar() !== $this) {
throw new LogicException('cannot remove document of another calendar');
throw new \LogicException('cannot remove document of another calendar');
}
return $this;
@@ -653,7 +685,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
return $this;
}
public function setEndDate(DateTimeImmutable $endDate): self
public function setEndDate(\DateTimeImmutable $endDate): self
{
if (null === $this->endDate || $this->endDate->getTimestamp() !== $endDate->getTimestamp()) {
$this->increaseaDatetimeVersion();
@@ -711,7 +743,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
return $this;
}
public function setStartDate(DateTimeImmutable $startDate): self
public function setStartDate(\DateTimeImmutable $startDate): self
{
if (null === $this->startDate || $this->startDate->getTimestamp() !== $startDate->getTimestamp()) {
$this->increaseaDatetimeVersion();
@@ -726,7 +758,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
{
$this->status = $status;
if (self::STATUS_CANCELED === $status && $this->getSmsStatus() === self::SMS_SENT) {
if (self::STATUS_CANCELED === $status && self::SMS_SENT === $this->getSmsStatus()) {
$this->setSmsStatus(self::SMS_CANCEL_PENDING);
}

View File

@@ -22,6 +22,7 @@ use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*
* @ORM\Table(
* name="chill_calendar.calendar_doc",
* )
@@ -34,6 +35,7 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\ManyToOne(targetEntity=Calendar::class, inversedBy="documents")
*
* @ORM\JoinColumn(nullable=false)
*/
private Calendar $calendar;
@@ -45,27 +47,26 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\Id
*
* @ORM\GeneratedValue
*
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=StoredObject::class, cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
*/
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, /**
* @ORM\ManyToOne(targetEntity=StoredObject::class, cascade={"persist"})
*
* @ORM\JoinColumn(nullable=false)
*/
private ?StoredObject $storedObject)
{
$this->setCalendar($calendar);
$this->storedObject = $storedObject;
$this->datetimeVersion = $calendar->getDateTimeVersion();
}
@@ -112,8 +113,6 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
/**
* @internal use @see{Calendar::removeDocument} instead
*
* @param Calendar $calendar
*/
public function setCalendar(?Calendar $calendar): CalendarDoc
{

View File

@@ -18,12 +18,14 @@ class CalendarDocCreateDTO
{
/**
* @Assert\NotNull
*
* @Assert\Valid
*/
public ?StoredObject $doc = null;
/**
* @Assert\NotBlank
*
* @Assert\NotNull
*/
public ?string $title = '';

View File

@@ -24,6 +24,7 @@ class CalendarDocEditDTO
/**
* @Assert\NotBlank
*
* @Assert\NotNull
*/
public ?string $title = '';

View File

@@ -17,7 +17,6 @@ use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
@@ -27,6 +26,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* name="chill_calendar.calendar_range",
* uniqueConstraints={@ORM\UniqueConstraint(name="idx_calendar_range_remote", columns={"remoteId"}, options={"where": "remoteId <> ''"})}
* )
*
* @ORM\Entity
*/
class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
@@ -44,37 +44,49 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\Column(type="datetime_immutable", nullable=false)
*
* @Groups({"read", "write", "calendar:read"})
*
* @Assert\NotNull
*/
private ?DateTimeImmutable $endDate = null;
private ?\DateTimeImmutable $endDate = null;
/**
* @ORM\Id
*
* @ORM\GeneratedValue
*
* @ORM\Column(type="integer")
*
* @Groups({"read"})
*/
private $id;
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=Location::class)
*
* @ORM\JoinColumn(nullable=false)
*
* @Groups({"read", "write", "calendar:read"})
*
* @Assert\NotNull
*/
private ?Location $location = null;
/**
* @ORM\Column(type="datetime_immutable", nullable=false)
*
* @groups({"read", "write", "calendar:read"})
*
* @Assert\NotNull
*/
private ?DateTimeImmutable $startDate = null;
private ?\DateTimeImmutable $startDate = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
*
* @Groups({"read", "write", "calendar:read"})
*
* @Assert\NotNull
*/
private ?User $user = null;
@@ -84,7 +96,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
return $this->calendar;
}
public function getEndDate(): ?DateTimeImmutable
public function getEndDate(): ?\DateTimeImmutable
{
return $this->endDate;
}
@@ -99,7 +111,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
return $this->location;
}
public function getStartDate(): ?DateTimeImmutable
public function getStartDate(): ?\DateTimeImmutable
{
return $this->startDate;
}
@@ -117,7 +129,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
$this->calendar = $calendar;
}
public function setEndDate(DateTimeImmutable $endDate): self
public function setEndDate(\DateTimeImmutable $endDate): self
{
$this->endDate = $endDate;
@@ -131,7 +143,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
return $this;
}
public function setStartDate(DateTimeImmutable $startDate): self
public function setStartDate(\DateTimeImmutable $startDate): self
{
$this->startDate = $startDate;

View File

@@ -16,37 +16,40 @@ use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="chill_calendar.cancel_reason")
*
* @ORM\Entity(repositoryClass=CancelReasonRepository::class)
*/
class CancelReason
{
public const CANCELEDBY_DONOTCOUNT = 'CANCELEDBY_DONOTCOUNT';
final public const CANCELEDBY_DONOTCOUNT = 'CANCELEDBY_DONOTCOUNT';
public const CANCELEDBY_PERSON = 'CANCELEDBY_PERSON';
final public const CANCELEDBY_PERSON = 'CANCELEDBY_PERSON';
public const CANCELEDBY_USER = 'CANCELEDBY_USER';
final public const CANCELEDBY_USER = 'CANCELEDBY_USER';
/**
* @ORM\Column(type="boolean")
*/
private $active;
private ?bool $active = null;
/**
* @ORM\Column(type="string", length=255)
*/
private $canceledBy;
private ?string $canceledBy = null;
/**
* @ORM\Id
*
* @ORM\GeneratedValue
*
* @ORM\Column(type="integer")
*/
private $id;
private ?int $id = null;
/**
* @ORM\Column(type="json")
*/
private $name = [];
private array $name = [];
public function getActive(): ?bool
{

View File

@@ -17,7 +17,6 @@ use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\Mapping as ORM;
use LogicException;
use Symfony\Component\Serializer\Annotation as Serializer;
/**
@@ -30,6 +29,7 @@ use Symfony\Component\Serializer\Annotation as Serializer;
* name="chill_calendar.invite",
* uniqueConstraints={@ORM\UniqueConstraint(name="idx_calendar_invite_remote", columns={"remoteId"}, options={"where": "remoteId <> ''"})}
* )
*
* @ORM\Entity
*/
class Invite implements TrackUpdateInterface, TrackCreationInterface
@@ -40,23 +40,23 @@ class Invite implements TrackUpdateInterface, TrackCreationInterface
use TrackUpdateTrait;
public const ACCEPTED = 'accepted';
final public const ACCEPTED = 'accepted';
public const DECLINED = 'declined';
final public const DECLINED = 'declined';
public const PENDING = 'pending';
final public const PENDING = 'pending';
/**
* all statuses in one const.
*/
public const STATUSES = [
final public const STATUSES = [
self::ACCEPTED,
self::DECLINED,
self::PENDING,
self::TENTATIVELY_ACCEPTED,
];
public const TENTATIVELY_ACCEPTED = 'tentative';
final public const TENTATIVELY_ACCEPTED = 'tentative';
/**
* @ORM\ManyToOne(targetEntity=Calendar::class, inversedBy="invites")
@@ -65,21 +65,27 @@ class Invite implements TrackUpdateInterface, TrackCreationInterface
/**
* @ORM\Id
*
* @ORM\GeneratedValue
*
* @ORM\Column(type="integer")
*
* @Serializer\Groups(groups={"calendar:read", "read"})
*/
private ?int $id = null;
/**
* @ORM\Column(type="text", nullable=false, options={"default": "pending"})
*
* @Serializer\Groups(groups={"calendar:read", "read", "docgen:read"})
*/
private string $status = self::PENDING;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
*
* @ORM\JoinColumn(nullable=false)
*
* @Serializer\Groups(groups={"calendar:read", "read", "docgen:read"})
*/
private ?User $user = null;
@@ -122,7 +128,7 @@ class Invite implements TrackUpdateInterface, TrackCreationInterface
public function setUser(?User $user): self
{
if ($user instanceof User && $this->user instanceof User && $user !== $this->user) {
throw new LogicException('Not allowed to associate an invite to a different user');
throw new \LogicException('Not allowed to associate an invite to a different user');
}
$this->user = $user;

View File

@@ -15,15 +15,10 @@ use Chill\ActivityBundle\Entity\Activity;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\HttpFoundation\RequestStack;
use function array_key_exists;
class ListenToActivityCreate
{
private RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
public function __construct(private readonly RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function postPersist(Activity $activity, LifecycleEventArgs $event): void
@@ -38,7 +33,7 @@ class ListenToActivityCreate
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('calendarId', $activityData)) {
if (\array_key_exists('calendarId', $activityData)) {
$calendarId = $activityData['calendarId'];
// Attach the activity to the calendar

View File

@@ -13,7 +13,7 @@ namespace Chill\CalendarBundle\Exception;
class UserAbsenceSyncException extends \LogicException
{
public function __construct(string $message = "", int $code = 20_230_706, ?\Throwable $previous = null)
public function __construct(string $message = '', int $code = 20_230_706, ?\Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}

View File

@@ -15,23 +15,13 @@ use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class AgentAggregator implements AggregatorInterface
final readonly class AgentAggregator implements AggregatorInterface
{
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
public function __construct(private UserRepository $userRepository, private UserRender $userRender)
{
}
public function addRole(): ?string
@@ -41,7 +31,7 @@ final class AgentAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
if (!\in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.mainUser', 'caluser');
}
@@ -58,12 +48,13 @@ final class AgentAggregator implements AggregatorInterface
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -15,23 +15,13 @@ use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CancelReasonRepository;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CancelReasonAggregator implements AggregatorInterface
{
private CancelReasonRepository $cancelReasonRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
CancelReasonRepository $cancelReasonRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->cancelReasonRepository = $cancelReasonRepository;
$this->translatableStringHelper = $translatableStringHelper;
public function __construct(private readonly CancelReasonRepository $cancelReasonRepository, private readonly TranslatableStringHelper $translatableStringHelper)
{
}
public function addRole(): ?string
@@ -42,7 +32,7 @@ class CancelReasonAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
// TODO: still needs to take into account calendars without a cancel reason somehow
if (!in_array('calcancel', $qb->getAllAliases(), true)) {
if (!\in_array('calcancel', $qb->getAllAliases(), true)) {
$qb->join('cal.cancelReason', 'calcancel');
}
@@ -59,12 +49,13 @@ class CancelReasonAggregator implements AggregatorInterface
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -12,26 +12,22 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Closure;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class JobAggregator implements AggregatorInterface
final readonly class JobAggregator implements AggregatorInterface
{
private UserJobRepository $jobRepository;
private TranslatableStringHelper $translatableStringHelper;
private const PREFIX = 'cal_agg_job';
public function __construct(
UserJobRepository $jobRepository,
TranslatableStringHelper $translatableStringHelper
private UserJobRepository $jobRepository,
private TranslatableStringHelper $translatableStringHelper
) {
$this->jobRepository = $jobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
@@ -41,12 +37,28 @@ final class JobAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.mainUser', 'caluser');
}
$p = self::PREFIX;
$qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator');
$qb->addGroupBy('job_aggregator');
$qb
->leftJoin('cal.mainUser', "{$p}_user")
->leftJoin(
UserJobHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// job_at based on cal.startDate
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'cal.startDate'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'cal.startDate')
)
)
)
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
->addGroupBy("{$p}_select");
}
public function applyOn(): string
@@ -56,14 +68,14 @@ final class JobAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {
@@ -74,7 +86,9 @@ final class JobAggregator implements AggregatorInterface
return '';
}
$j = $this->jobRepository->find($value);
if (null === $j = $this->jobRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize(
$j->getLabel()
@@ -84,11 +98,11 @@ final class JobAggregator implements AggregatorInterface
public function getQueryKeys($data): array
{
return ['job_aggregator'];
return [self::PREFIX.'_select'];
}
public function getTitle(): string
{
return 'Group calendars by agent job';
return 'export.aggregator.calendar.agent_job.Group calendars by agent job';
}
}

View File

@@ -14,19 +14,13 @@ namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationRepository;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class LocationAggregator implements AggregatorInterface
final readonly class LocationAggregator implements AggregatorInterface
{
private LocationRepository $locationRepository;
public function __construct(
LocationRepository $locationRepository
) {
$this->locationRepository = $locationRepository;
public function __construct(private LocationRepository $locationRepository)
{
}
public function addRole(): ?string
@@ -36,7 +30,7 @@ final class LocationAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('calloc', $qb->getAllAliases(), true)) {
if (!\in_array('calloc', $qb->getAllAliases(), true)) {
$qb->join('cal.location', 'calloc');
}
$qb->addSelect('IDENTITY(cal.location) as location_aggregator');
@@ -52,12 +46,13 @@ final class LocationAggregator implements AggregatorInterface
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -15,23 +15,13 @@ use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class LocationTypeAggregator implements AggregatorInterface
final readonly class LocationTypeAggregator implements AggregatorInterface
{
private LocationTypeRepository $locationTypeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
LocationTypeRepository $locationTypeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->locationTypeRepository = $locationTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
public function __construct(private LocationTypeRepository $locationTypeRepository, private TranslatableStringHelper $translatableStringHelper)
{
}
public function addRole(): ?string
@@ -41,7 +31,7 @@ final class LocationTypeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('calloc', $qb->getAllAliases(), true)) {
if (!\in_array('calloc', $qb->getAllAliases(), true)) {
$qb->join('cal.location', 'calloc');
}
@@ -58,12 +48,13 @@ final class LocationTypeAggregator implements AggregatorInterface
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -13,7 +13,6 @@ namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -40,12 +39,13 @@ class MonthYearAggregator implements AggregatorInterface
{
// No form needed
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return static function ($value): string {
if ('_header' === $value) {

View File

@@ -12,26 +12,22 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Closure;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ScopeAggregator implements AggregatorInterface
final readonly class ScopeAggregator implements AggregatorInterface
{
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
private const PREFIX = 'cal_agg_scope';
public function __construct(
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
private ScopeRepository $scopeRepository,
private TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
@@ -41,12 +37,28 @@ final class ScopeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.mainUser', 'caluser');
}
$p = self::PREFIX;
$qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator');
$qb->addGroupBy('scope_aggregator');
$qb
->leftJoin('cal.mainUser', "{$p}_user")
->leftJoin(
UserScopeHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// scope_at based on cal.startDate
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'cal.startDate'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'cal.startDate')
)
)
)
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
->addGroupBy("{$p}_select");
}
public function applyOn(): string
@@ -56,14 +68,14 @@ final class ScopeAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {
@@ -74,7 +86,9 @@ final class ScopeAggregator implements AggregatorInterface
return '';
}
$s = $this->scopeRepository->find($value);
if (null === $s = $this->scopeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize(
$s->getName()
@@ -84,11 +98,11 @@ final class ScopeAggregator implements AggregatorInterface
public function getQueryKeys($data): array
{
return ['scope_aggregator'];
return [self::PREFIX.'_select'];
}
public function getTitle(): string
{
return 'Group calendars by agent scope';
return 'export.aggregator.calendar.agent_scope.Group calendars by agent scope';
}
}

View File

@@ -20,20 +20,14 @@ namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class UrgencyAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
public function __construct(private readonly TranslatorInterface $translator)
{
}
public function addRole(): ?string
@@ -56,28 +50,24 @@ class UrgencyAggregator implements AggregatorInterface
{
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {
return 'Urgency';
}
switch ($value) {
case true:
return $this->translator->trans('is urgent');
case false:
return $this->translator->trans('is not urgent');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
return match ($value) {
true => $this->translator->trans('is urgent'),
false => $this->translator->trans('is not urgent'),
default => throw new \LogicException(sprintf('The value %s is not valid', $value)),
};
};
}

View File

@@ -16,5 +16,5 @@ namespace Chill\CalendarBundle\Export;
*/
abstract class Declarations
{
public const CALENDAR_TYPE = 'calendar';
final public const CALENDAR_TYPE = 'calendar';
}

View File

@@ -13,11 +13,11 @@ namespace Chill\CalendarBundle\Export\Export;
use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Closure;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -25,17 +25,16 @@ use Symfony\Component\Validator\Exception\LogicException;
class CountCalendars implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
public function __construct(CalendarRepository $calendarRepository)
{
$this->calendarRepository = $calendarRepository;
public function __construct(
private readonly CalendarRepository $calendarRepository,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// No form necessary
}
public function getFormDefaultData(): array
{
return [];
@@ -98,6 +97,9 @@ class CountCalendars implements ExportInterface, GroupedExportInterface
$qb = $this->calendarRepository->createQueryBuilder('cal');
$qb->select('COUNT(cal.id) AS export_result');
$qb->leftJoin('cal.accompanyingPeriod', 'acp');
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@@ -13,29 +13,26 @@ namespace Chill\CalendarBundle\Export\Export;
use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
public function __construct(
CalendarRepository $calendarRepository
) {
$this->calendarRepository = $calendarRepository;
public function __construct(private readonly CalendarRepository $calendarRepository)
{
}
public function buildForm(FormBuilderInterface $builder): void
{
// no form needed
}
public function getFormDefaultData(): array
{
return [];
@@ -59,7 +56,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
@@ -92,8 +89,10 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
{
$qb = $this->calendarRepository->createQueryBuilder('cal');
$qb
->select('AVG(cal.endDate - cal.startDate) AS export_result');
$qb->select('AVG(cal.endDate - cal.startDate) AS export_result');
$qb->join('cal.accompanyingPeriod', 'acp');
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@@ -13,29 +13,26 @@ namespace Chill\CalendarBundle\Export\Export;
use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
public function __construct(
CalendarRepository $calendarRepository
) {
$this->calendarRepository = $calendarRepository;
public function __construct(private readonly CalendarRepository $calendarRepository)
{
}
public function buildForm(FormBuilderInterface $builder): void
{
// no form needed
}
public function getFormDefaultData(): array
{
return [];
@@ -59,7 +56,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
@@ -92,8 +89,10 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
{
$qb = $this->calendarRepository->createQueryBuilder('cal');
$qb
->select('SUM(cal.endDate - cal.startDate) AS export_result');
$qb->select('SUM(cal.endDate - cal.startDate) AS export_result');
$qb->join('cal.accompanyingPeriod', 'acp');
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@@ -22,11 +22,8 @@ use Symfony\Component\Form\FormBuilderInterface;
class AgentFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
public function __construct(private readonly UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string
@@ -63,6 +60,7 @@ class AgentFilter implements FilterInterface
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return [];

View File

@@ -21,11 +21,8 @@ use Symfony\Component\Form\FormBuilderInterface;
class BetweenDatesFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
public function __construct(private readonly RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -63,6 +60,7 @@ class BetweenDatesFilter implements FilterInterface
->add('date_from', PickRollingDateType::class, [])
->add('date_to', PickRollingDateType::class, []);
}
public function getFormDefaultData(): array
{
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];

View File

@@ -20,7 +20,6 @@ namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -29,17 +28,14 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class CalendarRangeFilter implements FilterInterface
{
private const CHOICES = [
'Not made within a calendar range' => true,
'Made within a calendar range' => false,
'Not made within a calendar range' => 'true',
'Made within a calendar range' => 'false',
];
private const DEFAULT_CHOICE = false;
private const DEFAULT_CHOICE = 'false';
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
public function __construct(private readonly TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
@@ -71,6 +67,7 @@ class CalendarRangeFilter implements FilterInterface
'empty_data' => self::DEFAULT_CHOICE,
]);
}
public function getFormDefaultData(): array
{
return ['hasCalendarRange' => self::DEFAULT_CHOICE];

View File

@@ -12,28 +12,24 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
class JobFilter implements FilterInterface
final readonly class JobFilter implements FilterInterface
{
protected TranslatorInterface $translator;
private TranslatableStringHelper $translatableStringHelper;
private const PREFIX = 'cal_filter_job';
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper
private TranslatableStringHelper $translatableStringHelper,
private UserJobRepositoryInterface $userJobRepository
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
@@ -43,21 +39,31 @@ class JobFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.mainUser', 'caluser');
}
$p = self::PREFIX;
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('caluser.userJob', ':job');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('job', $data['job']);
$qb
->leftJoin('cal.mainUser', "{$p}_user")
->leftJoin(
UserJobHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// job_at based on cal.startDate
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'cal.startDate'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'cal.startDate')
)
)
)
->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_job"))
->setParameter(
"{$p}_job",
$data['job']
);
}
public function applyOn(): string
@@ -67,18 +73,16 @@ class JobFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('job', EntityType::class, [
'class' => UserJob::class,
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
$j->getLabel()
),
'multiple' => true,
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return [];
$builder
->add('job', EntityType::class, [
'class' => UserJob::class,
'choices' => $this->userJobRepository->findAllActive(),
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
$j->getLabel()
),
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string'): array
@@ -91,13 +95,20 @@ class JobFilter implements FilterInterface
);
}
return ['Filtered by agent job: only %jobs%', [
return ['export.filter.calendar.agent_job.Filtered by agent job: only %jobs%', [
'%jobs%' => implode(', ', $userJobs),
]];
}
public function getFormDefaultData(): array
{
return [
'job' => [],
];
}
public function getTitle(): string
{
return 'Filter calendars by agent job';
return 'export.filter.calendar.agent_job.Filter calendars by agent job';
}
}

View File

@@ -13,27 +13,25 @@ namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
class ScopeFilter implements FilterInterface
{
protected TranslatorInterface $translator;
private TranslatableStringHelper $translatableStringHelper;
private const PREFIX = 'cal_filter_scope';
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper
protected TranslatorInterface $translator,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly ScopeRepositoryInterface $scopeRepository
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
@@ -43,45 +41,53 @@ class ScopeFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.mainUser', 'caluser');
}
$p = self::PREFIX;
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('caluser.mainScope', ':scope');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('scope', $data['scope']);
$qb
->leftJoin('cal.mainUser', "{$p}_user")
->leftJoin(
UserScopeHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// scope_at based on cal.startDate
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'cal.startDate'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'cal.startDate')
)
)
)
->andWhere($qb->expr()->in("{$p}_history.scope", ":{$p}_scope"))
->setParameter(
"{$p}_scope",
$data['scope']
);
}
public function applyOn()
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('scope', EntityType::class, [
'class' => Scope::class,
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
$s->getName()
),
'multiple' => true,
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return [];
$builder
->add('scope', EntityType::class, [
'class' => Scope::class,
'choices' => $this->scopeRepository->findAllActive(),
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
$s->getName()
),
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
public function describeAction($data, $format = 'string'): array
{
$scopes = [];
@@ -91,13 +97,20 @@ class ScopeFilter implements FilterInterface
);
}
return ['Filtered by agent scope: only %scopes%', [
return ['export.filter.calendar.agent_scope.Filtered by agent scope: only %scopes%', [
'%scopes%' => implode(', ', $scopes),
]];
}
public function getTitle()
public function getFormDefaultData(): array
{
return 'Filter calendars by agent scope';
return [
'scope' => [],
];
}
public function getTitle(): string
{
return 'export.filter.calendar.agent_scope.Filter calendars by agent scope';
}
}

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
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;

View File

@@ -21,7 +21,6 @@ use Chill\MainBundle\Form\Type\CommentType;
use Chill\MainBundle\Form\Type\PrivateCommentType;
use Chill\PersonBundle\Form\DataTransformer\PersonsToIdDataTransformer;
use Chill\ThirdPartyBundle\Form\DataTransformer\ThirdPartiesToIdDataTransformer;
use DateTimeImmutable;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
@@ -32,32 +31,14 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class CalendarType extends AbstractType
{
private IdToCalendarRangeDataTransformer $calendarRangeDataTransformer;
private IdToLocationDataTransformer $idToLocationDataTransformer;
private IdToUserDataTransformer $idToUserDataTransformer;
private IdToUsersDataTransformer $idToUsersDataTransformer;
private ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer;
private PersonsToIdDataTransformer $personsToIdDataTransformer;
public function __construct(
PersonsToIdDataTransformer $personsToIdDataTransformer,
IdToUserDataTransformer $idToUserDataTransformer,
IdToUsersDataTransformer $idToUsersDataTransformer,
IdToLocationDataTransformer $idToLocationDataTransformer,
ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer,
IdToCalendarRangeDataTransformer $idToCalendarRangeDataTransformer
private readonly PersonsToIdDataTransformer $personsToIdDataTransformer,
private readonly IdToUserDataTransformer $idToUserDataTransformer,
private readonly IdToUsersDataTransformer $idToUsersDataTransformer,
private readonly IdToLocationDataTransformer $idToLocationDataTransformer,
private readonly ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer,
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer
) {
$this->personsToIdDataTransformer = $personsToIdDataTransformer;
$this->idToUserDataTransformer = $idToUserDataTransformer;
$this->idToUsersDataTransformer = $idToUsersDataTransformer;
$this->idToLocationDataTransformer = $idToLocationDataTransformer;
$this->partiesToIdDataTransformer = $partiesToIdDataTransformer;
$this->calendarRangeDataTransformer = $idToCalendarRangeDataTransformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -91,42 +72,42 @@ class CalendarType extends AbstractType
$builder->add('startDate', HiddenType::class);
$builder->get('startDate')
->addModelTransformer(new CallbackTransformer(
static function (?DateTimeImmutable $dateTimeImmutable): string {
static function (?\DateTimeImmutable $dateTimeImmutable): string {
if (null !== $dateTimeImmutable) {
$res = date_format($dateTimeImmutable, DateTimeImmutable::ATOM);
$res = date_format($dateTimeImmutable, \DateTimeImmutable::ATOM);
} else {
$res = '';
}
return $res;
},
static function (?string $dateAsString): ?DateTimeImmutable {
static function (?string $dateAsString): ?\DateTimeImmutable {
if ('' === $dateAsString || null === $dateAsString) {
return null;
}
return DateTimeImmutable::createFromFormat(DateTimeImmutable::ATOM, $dateAsString);
return \DateTimeImmutable::createFromFormat(\DateTimeImmutable::ATOM, $dateAsString);
}
));
$builder->add('endDate', HiddenType::class);
$builder->get('endDate')
->addModelTransformer(new CallbackTransformer(
static function (?DateTimeImmutable $dateTimeImmutable): string {
static function (?\DateTimeImmutable $dateTimeImmutable): string {
if (null !== $dateTimeImmutable) {
$res = date_format($dateTimeImmutable, DateTimeImmutable::ATOM);
$res = date_format($dateTimeImmutable, \DateTimeImmutable::ATOM);
} else {
$res = '';
}
return $res;
},
static function (?string $dateAsString): ?DateTimeImmutable {
static function (?string $dateAsString): ?\DateTimeImmutable {
if ('' === $dateAsString || null === $dateAsString) {
return null;
}
return DateTimeImmutable::createFromFormat(DateTimeImmutable::ATOM, $dateAsString);
return \DateTimeImmutable::createFromFormat(\DateTimeImmutable::ATOM, $dateAsString);
}
));

View File

@@ -19,16 +19,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
{
protected TranslatorInterface $translator;
private Security $security;
public function __construct(
Security $security,
TranslatorInterface $translator
) {
$this->security = $security;
$this->translator = $translator;
public function __construct(private readonly Security $security, protected TranslatorInterface $translator)
{
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)

View File

@@ -19,16 +19,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class PersonMenuBuilder implements LocalMenuBuilderInterface
{
protected TranslatorInterface $translator;
private Security $security;
public function __construct(
Security $security,
TranslatorInterface $translator
) {
$this->security = $security;
$this->translator = $translator;
public function __construct(private readonly Security $security, protected TranslatorInterface $translator)
{
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)

View File

@@ -18,16 +18,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class UserMenuBuilder implements LocalMenuBuilderInterface
{
public TranslatorInterface $translator;
private Security $security;
public function __construct(
Security $security,
TranslatorInterface $translator
) {
$this->security = $security;
$this->translator = $translator;
public function __construct(private readonly Security $security, public TranslatorInterface $translator)
{
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)

View File

@@ -21,7 +21,6 @@ namespace Chill\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Messenger\Message\CalendarMessage;
use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostPersistEventArgs;
use Doctrine\ORM\Event\PostRemoveEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;
@@ -30,14 +29,8 @@ use Symfony\Component\Security\Core\Security;
class CalendarEntityListener
{
private MessageBusInterface $messageBus;
private Security $security;
public function __construct(MessageBusInterface $messageBus, Security $security)
public function __construct(private readonly MessageBusInterface $messageBus, private readonly Security $security)
{
$this->messageBus = $messageBus;
$this->security = $security;
}
public function postPersist(Calendar $calendar, PostPersistEventArgs $args): void

View File

@@ -21,7 +21,6 @@ namespace Chill\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\CalendarBundle\Messenger\Message\CalendarRangeMessage;
use Chill\CalendarBundle\Messenger\Message\CalendarRangeRemovedMessage;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostPersistEventArgs;
use Doctrine\ORM\Event\PostRemoveEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;
@@ -30,14 +29,8 @@ use Symfony\Component\Security\Core\Security;
class CalendarRangeEntityListener
{
private MessageBusInterface $messageBus;
private Security $security;
public function __construct(MessageBusInterface $messageBus, Security $security)
public function __construct(private readonly MessageBusInterface $messageBus, private readonly Security $security)
{
$this->messageBus = $messageBus;
$this->security = $security;
}
public function postPersist(CalendarRange $calendarRange, PostPersistEventArgs $eventArgs): void

View File

@@ -31,14 +31,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
*/
class CalendarRangeRemoveToRemoteHandler implements MessageHandlerInterface
{
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
private UserRepository $userRepository;
public function __construct(RemoteCalendarConnectorInterface $remoteCalendarConnector, UserRepository $userRepository)
public function __construct(private readonly RemoteCalendarConnectorInterface $remoteCalendarConnector, private readonly UserRepository $userRepository)
{
$this->remoteCalendarConnector = $remoteCalendarConnector;
$this->userRepository = $userRepository;
}
public function __invoke(CalendarRangeRemovedMessage $calendarRangeRemovedMessage)

View File

@@ -32,20 +32,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
*/
class CalendarRangeToRemoteHandler implements MessageHandlerInterface
{
private CalendarRangeRepository $calendarRangeRepository;
private EntityManagerInterface $entityManager;
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
public function __construct(
CalendarRangeRepository $calendarRangeRepository,
RemoteCalendarConnectorInterface $remoteCalendarConnector,
EntityManagerInterface $entityManager
) {
$this->calendarRangeRepository = $calendarRangeRepository;
$this->remoteCalendarConnector = $remoteCalendarConnector;
$this->entityManager = $entityManager;
public function __construct(private readonly CalendarRangeRepository $calendarRangeRepository, private readonly RemoteCalendarConnectorInterface $remoteCalendarConnector, private readonly EntityManagerInterface $entityManager)
{
}
public function __invoke(CalendarRangeMessage $calendarRangeMessage): void

View File

@@ -31,17 +31,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
*/
class CalendarRemoveHandler implements MessageHandlerInterface
{
private CalendarRangeRepository $calendarRangeRepository;
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
private UserRepositoryInterface $userRepository;
public function __construct(RemoteCalendarConnectorInterface $remoteCalendarConnector, CalendarRangeRepository $calendarRangeRepository, UserRepositoryInterface $userRepository)
public function __construct(private readonly RemoteCalendarConnectorInterface $remoteCalendarConnector, private readonly CalendarRangeRepository $calendarRangeRepository, private readonly UserRepositoryInterface $userRepository)
{
$this->remoteCalendarConnector = $remoteCalendarConnector;
$this->calendarRangeRepository = $calendarRangeRepository;
$this->userRepository = $userRepository;
}
public function __invoke(CalendarRemovedMessage $message)

View File

@@ -37,32 +37,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
*/
class CalendarToRemoteHandler implements MessageHandlerInterface
{
private RemoteCalendarConnectorInterface $calendarConnector;
private CalendarRangeRepository $calendarRangeRepository;
private CalendarRepository $calendarRepository;
private EntityManagerInterface $entityManager;
private InviteRepository $inviteRepository;
private UserRepository $userRepository;
public function __construct(
CalendarRangeRepository $calendarRangeRepository,
CalendarRepository $calendarRepository,
EntityManagerInterface $entityManager,
InviteRepository $inviteRepository,
RemoteCalendarConnectorInterface $calendarConnector,
UserRepository $userRepository
) {
$this->calendarConnector = $calendarConnector;
$this->calendarRepository = $calendarRepository;
$this->calendarRangeRepository = $calendarRangeRepository;
$this->entityManager = $entityManager;
$this->userRepository = $userRepository;
$this->inviteRepository = $inviteRepository;
public function __construct(private readonly CalendarRangeRepository $calendarRangeRepository, private readonly CalendarRepository $calendarRepository, private readonly EntityManagerInterface $entityManager, private readonly InviteRepository $inviteRepository, private readonly RemoteCalendarConnectorInterface $calendarConnector, private readonly UserRepository $userRepository)
{
}
public function __invoke(CalendarMessage $calendarMessage)

View File

@@ -31,17 +31,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
*/
class InviteUpdateHandler implements MessageHandlerInterface
{
private EntityManagerInterface $em;
private InviteRepository $inviteRepository;
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
public function __construct(EntityManagerInterface $em, InviteRepository $inviteRepository, RemoteCalendarConnectorInterface $remoteCalendarConnector)
public function __construct(private readonly EntityManagerInterface $em, private readonly InviteRepository $inviteRepository, private readonly RemoteCalendarConnectorInterface $remoteCalendarConnector)
{
$this->em = $em;
$this->inviteRepository = $inviteRepository;
$this->remoteCalendarConnector = $remoteCalendarConnector;
}
public function __invoke(InviteUpdateMessage $inviteUpdateMessage): void

View File

@@ -36,40 +36,8 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
*/
class MSGraphChangeNotificationHandler implements MessageHandlerInterface
{
private CalendarRangeRepository $calendarRangeRepository;
private CalendarRangeSyncer $calendarRangeSyncer;
private CalendarRepository $calendarRepository;
private CalendarSyncer $calendarSyncer;
private EntityManagerInterface $em;
private LoggerInterface $logger;
private MapCalendarToUser $mapCalendarToUser;
private UserRepository $userRepository;
public function __construct(
CalendarRangeRepository $calendarRangeRepository,
CalendarRangeSyncer $calendarRangeSyncer,
CalendarRepository $calendarRepository,
CalendarSyncer $calendarSyncer,
EntityManagerInterface $em,
LoggerInterface $logger,
MapCalendarToUser $mapCalendarToUser,
UserRepository $userRepository
) {
$this->calendarRangeRepository = $calendarRangeRepository;
$this->calendarRangeSyncer = $calendarRangeSyncer;
$this->calendarRepository = $calendarRepository;
$this->calendarSyncer = $calendarSyncer;
$this->em = $em;
$this->logger = $logger;
$this->mapCalendarToUser = $mapCalendarToUser;
$this->userRepository = $userRepository;
public function __construct(private readonly CalendarRangeRepository $calendarRangeRepository, private readonly CalendarRangeSyncer $calendarRangeSyncer, private readonly CalendarRepository $calendarRepository, private readonly CalendarSyncer $calendarSyncer, private readonly EntityManagerInterface $em, private readonly LoggerInterface $logger, private readonly MapCalendarToUser $mapCalendarToUser, private readonly UserRepository $userRepository)
{
}
public function __invoke(MSGraphChangeNotificationMessage $changeNotificationMessage): void
@@ -77,7 +45,7 @@ class MSGraphChangeNotificationHandler implements MessageHandlerInterface
$user = $this->userRepository->find($changeNotificationMessage->getUserId());
if (null === $user) {
$this->logger->warning(self::class . ' notification concern non-existent user, skipping');
$this->logger->warning(self::class.' notification concern non-existent user, skipping');
return;
}
@@ -86,7 +54,7 @@ class MSGraphChangeNotificationHandler implements MessageHandlerInterface
$secret = $this->mapCalendarToUser->getSubscriptionSecret($user);
if ($secret !== ($notification['clientState'] ?? -1)) {
$this->logger->warning(self::class . ' could not validate secret, skipping');
$this->logger->warning(self::class.' could not validate secret, skipping');
continue;
}
@@ -101,7 +69,7 @@ class MSGraphChangeNotificationHandler implements MessageHandlerInterface
$this->calendarSyncer->handleCalendarSync($calendar, $notification, $user);
$this->em->flush();
} else {
$this->logger->info(self::class . ' id not found in any calendar nor calendar range');
$this->logger->info(self::class.' id not found in any calendar nor calendar range');
}
}

View File

@@ -24,15 +24,13 @@ use Chill\MainBundle\Entity\User;
class CalendarMessage
{
public const CALENDAR_PERSIST = 'CHILL_CALENDAR_CALENDAR_PERSIST';
final public const CALENDAR_PERSIST = 'CHILL_CALENDAR_CALENDAR_PERSIST';
public const CALENDAR_UPDATE = 'CHILL_CALENDAR_CALENDAR_UPDATE';
final public const CALENDAR_UPDATE = 'CHILL_CALENDAR_CALENDAR_UPDATE';
private string $action;
private readonly int $byUserId;
private int $byUserId;
private int $calendarId;
private readonly int $calendarId;
private array $newInvitesIds = [];
@@ -47,12 +45,11 @@ class CalendarMessage
public function __construct(
Calendar $calendar,
string $action,
private readonly string $action,
User $byUser
) {
$this->calendarId = $calendar->getId();
$this->byUserId = $byUser->getId();
$this->action = $action;
$this->previousCalendarRangeId = null !== $calendar->previousCalendarRange ?
$calendar->previousCalendarRange->getId() : null;
$this->previousMainUserId = null !== $calendar->previousMainUser ?

View File

@@ -23,19 +23,16 @@ use Chill\MainBundle\Entity\User;
class CalendarRangeMessage
{
public const CALENDAR_RANGE_PERSIST = 'CHILL_CALENDAR_CALENDAR_RANGE_PERSIST';
final public const CALENDAR_RANGE_PERSIST = 'CHILL_CALENDAR_CALENDAR_RANGE_PERSIST';
public const CALENDAR_RANGE_UPDATE = 'CHILL_CALENDAR_CALENDAR_RANGE_UPDATE';
private string $action;
final public const CALENDAR_RANGE_UPDATE = 'CHILL_CALENDAR_CALENDAR_RANGE_UPDATE';
private ?int $byUserId = null;
private int $calendarRangeId;
private readonly int $calendarRangeId;
public function __construct(CalendarRange $calendarRange, string $action, ?User $byUser)
public function __construct(CalendarRange $calendarRange, private readonly string $action, ?User $byUser)
{
$this->action = $action;
$this->calendarRangeId = $calendarRange->getId();
if (null !== $byUser) {

View File

@@ -25,11 +25,11 @@ class CalendarRangeRemovedMessage
{
private ?int $byUserId = null;
private int $calendarRangeUserId;
private readonly int $calendarRangeUserId;
private array $remoteAttributes;
private readonly array $remoteAttributes;
private string $remoteId;
private readonly string $remoteId;
public function __construct(CalendarRange $calendarRange, ?User $byUser)
{

View File

@@ -27,11 +27,11 @@ class CalendarRemovedMessage
private ?int $byUserId = null;
private int $calendarUserId;
private readonly int $calendarUserId;
private array $remoteAttributes;
private readonly array $remoteAttributes;
private string $remoteId;
private readonly string $remoteId;
public function __construct(Calendar $calendar, ?User $byUser)
{

View File

@@ -23,9 +23,9 @@ use Chill\MainBundle\Entity\User;
class InviteUpdateMessage
{
private int $byUserId;
private readonly int $byUserId;
private int $inviteId;
private readonly int $inviteId;
public function __construct(Invite $invite, User $byUser)
{

View File

@@ -20,14 +20,8 @@ namespace Chill\CalendarBundle\Messenger\Message;
class MSGraphChangeNotificationMessage
{
private array $content;
private int $userId;
public function __construct(array $content, int $userId)
public function __construct(private readonly array $content, private readonly int $userId)
{
$this->content = $content;
$this->userId = $userId;
}
public function getContent(): array

View File

@@ -24,14 +24,8 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
class AddressConverter
{
private AddressRender $addressRender;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(AddressRender $addressRender, TranslatableStringHelperInterface $translatableStringHelper)
public function __construct(private readonly AddressRender $addressRender, private readonly TranslatableStringHelperInterface $translatableStringHelper)
{
$this->addressRender = $addressRender;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addressToRemote(Address $address): array

View File

@@ -19,8 +19,6 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use LogicException;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
@@ -30,39 +28,23 @@ use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
*/
class EventsOnUserSubscriptionCreator
{
private LoggerInterface $logger;
private MachineHttpClient $machineHttpClient;
private MapCalendarToUser $mapCalendarToUser;
private UrlGeneratorInterface $urlGenerator;
public function __construct(
LoggerInterface $logger,
MachineHttpClient $machineHttpClient,
MapCalendarToUser $mapCalendarToUser,
UrlGeneratorInterface $urlGenerator
) {
$this->logger = $logger;
$this->machineHttpClient = $machineHttpClient;
$this->mapCalendarToUser = $mapCalendarToUser;
$this->urlGenerator = $urlGenerator;
public function __construct(private readonly LoggerInterface $logger, private readonly MachineHttpClient $machineHttpClient, private readonly MapCalendarToUser $mapCalendarToUser, private readonly UrlGeneratorInterface $urlGenerator)
{
}
/**
* @return array{secret: string, id: string, expiration: int}
*
* @throws ClientExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
*
* @return array<secret: string, id: string, expiration: int>
*/
public function createSubscriptionForUser(User $user, DateTimeImmutable $expiration): array
public function createSubscriptionForUser(User $user, \DateTimeImmutable $expiration): array
{
if (null === $userId = $this->mapCalendarToUser->getUserId($user)) {
throw new LogicException('no user id');
throw new \LogicException('no user id');
}
$subscription = [
@@ -74,7 +56,7 @@ class EventsOnUserSubscriptionCreator
),
'resource' => "/users/{$userId}/calendar/events",
'clientState' => $secret = base64_encode(openssl_random_pseudo_bytes(92, $cstrong)),
'expirationDateTime' => $expiration->format(DateTimeImmutable::ATOM),
'expirationDateTime' => $expiration->format(\DateTimeImmutable::ATOM),
];
try {
@@ -97,26 +79,26 @@ class EventsOnUserSubscriptionCreator
}
/**
* @return array{secret: string, id: string, expiration: int}
*
* @throws ClientExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
*
* @return array<secret: string, id: string, expiration: int>
*/
public function renewSubscriptionForUser(User $user, DateTimeImmutable $expiration): array
public function renewSubscriptionForUser(User $user, \DateTimeImmutable $expiration): array
{
if (null === $userId = $this->mapCalendarToUser->getUserId($user)) {
throw new LogicException('no user id');
throw new \LogicException('no user id');
}
if (null === $subscriptionId = $this->mapCalendarToUser->getActiveSubscriptionId($user)) {
throw new LogicException('no user id');
throw new \LogicException('no user id');
}
$subscription = [
'expirationDateTime' => $expiration->format(DateTimeImmutable::ATOM),
'expirationDateTime' => $expiration->format(\DateTimeImmutable::ATOM),
];
try {

View File

@@ -22,11 +22,8 @@ use Chill\MainBundle\Entity\Location;
class LocationConverter
{
private AddressConverter $addressConverter;
public function __construct(AddressConverter $addressConverter)
public function __construct(private readonly AddressConverter $addressConverter)
{
$this->addressConverter = $addressConverter;
}
public function locationToRemote(Location $location): array

View File

@@ -13,7 +13,6 @@ namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\CalendarBundle\Exception\UserAbsenceSyncException;
use Chill\MainBundle\Entity\User;
use Psr\Log\LoggerInterface;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
@@ -34,7 +33,7 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface
/**
* @throw UserAbsenceSyncException when the data cannot be reached or is not valid from microsoft
*/
public function isUserAbsent(User $user): bool|null
public function isUserAbsent(User $user): ?bool
{
$id = $this->mapCalendarToUser->getUserId($user);
@@ -44,26 +43,24 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface
try {
$automaticRepliesSettings = $this->machineHttpClient
->request('GET', 'users/' . $id . '/mailboxSettings/automaticRepliesSetting')
->request('GET', 'users/'.$id.'/mailboxSettings/automaticRepliesSetting')
->toArray(true);
} catch (ClientExceptionInterface|DecodingExceptionInterface|RedirectionExceptionInterface|TransportExceptionInterface $e) {
throw new UserAbsenceSyncException("Error receiving response for mailboxSettings", 0, $e);
throw new UserAbsenceSyncException('Error receiving response for mailboxSettings', 0, $e);
} catch (ServerExceptionInterface $e) {
throw new UserAbsenceSyncException("Server error receiving response for mailboxSettings", 0, $e);
throw new UserAbsenceSyncException('Server error receiving response for mailboxSettings', 0, $e);
}
if (!array_key_exists("status", $automaticRepliesSettings)) {
throw new \LogicException("no key \"status\" on automatic replies settings: " . json_encode($automaticRepliesSettings, JSON_THROW_ON_ERROR));
if (!array_key_exists('status', $automaticRepliesSettings)) {
throw new \LogicException('no key "status" on automatic replies settings: '.json_encode($automaticRepliesSettings, JSON_THROW_ON_ERROR));
}
return match ($automaticRepliesSettings['status']) {
'disabled' => false,
'alwaysEnabled' => true,
'scheduled' =>
RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['dateTime']) < $this->clock->now()
'scheduled' => RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['dateTime']) < $this->clock->now()
&& RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledEndDateTime']['dateTime']) > $this->clock->now(),
default => throw new UserAbsenceSyncException("this status is not documented by Microsoft")
default => throw new UserAbsenceSyncException('this status is not documented by Microsoft')
};
}
}

View File

@@ -18,5 +18,5 @@ interface MSUserAbsenceReaderInterface
/**
* @throw UserAbsenceSyncException when the data cannot be reached or is not valid from microsoft
*/
public function isUserAbsent(User $user): bool|null;
public function isUserAbsent(User $user): ?bool;
}

View File

@@ -37,13 +37,13 @@ readonly class MSUserAbsenceSync
return;
}
$this->logger->info("will change user absence", ['userId' => $user->getId()]);
$this->logger->info('will change user absence', ['userId' => $user->getId()]);
if ($absence) {
$this->logger->debug("make user absent", ['userId' => $user->getId()]);
$this->logger->debug('make user absent', ['userId' => $user->getId()]);
$user->setAbsenceStart($this->clock->now());
} else {
$this->logger->debug("make user present", ['userId' => $user->getId()]);
$this->logger->debug('make user present', ['userId' => $user->getId()]);
$user->setAbsenceStart(null);
}
}

View File

@@ -19,7 +19,6 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use LogicException;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
@@ -28,22 +27,16 @@ class MachineHttpClient implements HttpClientInterface
{
use BearerAuthorizationTrait;
private HttpClientInterface $decoratedClient;
private readonly HttpClientInterface $decoratedClient;
private MachineTokenStorage $machineTokenStorage;
/**
* @param HttpClientInterface $decoratedClient
*/
public function __construct(MachineTokenStorage $machineTokenStorage, ?HttpClientInterface $decoratedClient = null)
public function __construct(private readonly MachineTokenStorage $machineTokenStorage, ?HttpClientInterface $decoratedClient = null)
{
$this->decoratedClient = $decoratedClient ?? \Symfony\Component\HttpClient\HttpClient::create();
$this->machineTokenStorage = $machineTokenStorage;
}
/**
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
* @throws LogicException if method is not supported
* @throws \LogicException if method is not supported
*/
public function request(string $method, string $url, array $options = []): ResponseInterface
{
@@ -69,7 +62,7 @@ class MachineHttpClient implements HttpClientInterface
break;
default:
throw new LogicException("Method not supported: {$method}");
throw new \LogicException("Method not supported: {$method}");
}
return $this->decoratedClient->request($method, $url, $options);

View File

@@ -29,14 +29,8 @@ class MachineTokenStorage
private ?AccessTokenInterface $accessToken = null;
private Azure $azure;
private ChillRedis $chillRedis;
public function __construct(Azure $azure, ChillRedis $chillRedis)
public function __construct(private readonly Azure $azure, private readonly ChillRedis $chillRedis)
{
$this->azure = $azure;
$this->chillRedis = $chillRedis;
}
public function getToken(): AccessTokenInterface

View File

@@ -19,46 +19,35 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use LogicException;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use function array_key_exists;
/**
* Write metadata to user, which allow to find his default calendar.
*/
class MapCalendarToUser
{
public const EXPIRATION_SUBSCRIPTION_EVENT = 'subscription_events_expiration';
final public const EXPIRATION_SUBSCRIPTION_EVENT = 'subscription_events_expiration';
public const ID_SUBSCRIPTION_EVENT = 'subscription_events_id';
final public const ID_SUBSCRIPTION_EVENT = 'subscription_events_id';
public const METADATA_KEY = 'msgraph';
final public const METADATA_KEY = 'msgraph';
public const SECRET_SUBSCRIPTION_EVENT = 'subscription_events_secret';
final public const SECRET_SUBSCRIPTION_EVENT = 'subscription_events_secret';
private LoggerInterface $logger;
private HttpClientInterface $machineHttpClient;
public function __construct(
HttpClientInterface $machineHttpClient,
LoggerInterface $logger
) {
$this->machineHttpClient = $machineHttpClient;
$this->logger = $logger;
public function __construct(private readonly HttpClientInterface $machineHttpClient, private readonly LoggerInterface $logger)
{
}
public function getActiveSubscriptionId(User $user): string
{
if (!array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
throw new LogicException('do not contains msgraph metadata');
if (!\array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
throw new \LogicException('do not contains msgraph metadata');
}
if (!array_key_exists(self::ID_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY])) {
throw new LogicException('do not contains metadata for subscription id');
if (!\array_key_exists(self::ID_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY])) {
throw new \LogicException('do not contains metadata for subscription id');
}
return $user->getAttributes()[self::METADATA_KEY][self::ID_SUBSCRIPTION_EVENT];
@@ -93,12 +82,12 @@ class MapCalendarToUser
public function getSubscriptionSecret(User $user): string
{
if (!array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
throw new LogicException('do not contains msgraph metadata');
if (!\array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
throw new \LogicException('do not contains msgraph metadata');
}
if (!array_key_exists(self::SECRET_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY])) {
throw new LogicException('do not contains secret in msgraph');
if (!\array_key_exists(self::SECRET_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY])) {
throw new \LogicException('do not contains secret in msgraph');
}
return $user->getAttributes()[self::METADATA_KEY][self::SECRET_SUBSCRIPTION_EVENT];
@@ -124,25 +113,25 @@ class MapCalendarToUser
public function hasActiveSubscription(User $user): bool
{
if (!array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
if (!\array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
return false;
}
if (!array_key_exists(self::EXPIRATION_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY])) {
if (!\array_key_exists(self::EXPIRATION_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY])) {
return false;
}
return $user->getAttributes()[self::METADATA_KEY][self::EXPIRATION_SUBSCRIPTION_EVENT]
>= (new DateTimeImmutable('now'))->getTimestamp();
>= (new \DateTimeImmutable('now'))->getTimestamp();
}
public function hasSubscriptionSecret(User $user): bool
{
if (!array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
if (!\array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
return false;
}
return array_key_exists(self::SECRET_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY]);
return \array_key_exists(self::SECRET_SUBSCRIPTION_EVENT, $user->getAttributes()[self::METADATA_KEY]);
}
public function hasUserId(User $user): bool
@@ -151,11 +140,11 @@ class MapCalendarToUser
return false;
}
if (!array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
if (!\array_key_exists(self::METADATA_KEY, $user->getAttributes())) {
return false;
}
return array_key_exists('id', $user->getAttributes()[self::METADATA_KEY]);
return \array_key_exists('id', $user->getAttributes()[self::METADATA_KEY]);
}
public function writeMetadata(User $user): User

View File

@@ -19,7 +19,6 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use LogicException;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
@@ -28,17 +27,11 @@ class OnBehalfOfUserHttpClient
{
use BearerAuthorizationTrait;
private HttpClientInterface $decoratedClient;
private readonly HttpClientInterface $decoratedClient;
private OnBehalfOfUserTokenStorage $tokenStorage;
/**
* @param HttpClientInterface $decoratedClient
*/
public function __construct(OnBehalfOfUserTokenStorage $tokenStorage, ?HttpClientInterface $decoratedClient = null)
public function __construct(private readonly OnBehalfOfUserTokenStorage $tokenStorage, ?HttpClientInterface $decoratedClient = null)
{
$this->decoratedClient = $decoratedClient ?? \Symfony\Component\HttpClient\HttpClient::create();
$this->tokenStorage = $tokenStorage;
}
public function request(string $method, string $url, array $options = []): ResponseInterface
@@ -64,7 +57,7 @@ class OnBehalfOfUserHttpClient
break;
default:
throw new LogicException("Method not supported: {$method}");
throw new \LogicException("Method not supported: {$method}");
}
return $this->decoratedClient->request($method, $url, $options);

View File

@@ -18,7 +18,6 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use LogicException;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use TheNetworg\OAuth2\Client\Provider\Azure;
use TheNetworg\OAuth2\Client\Token\AccessToken;
@@ -28,16 +27,10 @@ use TheNetworg\OAuth2\Client\Token\AccessToken;
*/
class OnBehalfOfUserTokenStorage
{
public const MS_GRAPH_ACCESS_TOKEN = 'msgraph_access_token';
final public const MS_GRAPH_ACCESS_TOKEN = 'msgraph_access_token';
private Azure $azure;
private SessionInterface $session;
public function __construct(Azure $azure, SessionInterface $session)
public function __construct(private readonly Azure $azure, private readonly SessionInterface $session)
{
$this->azure = $azure;
$this->session = $session;
}
public function getToken(): AccessToken
@@ -46,7 +39,7 @@ class OnBehalfOfUserTokenStorage
$token = $this->session->get(self::MS_GRAPH_ACCESS_TOKEN, null);
if (null === $token) {
throw new LogicException('unexisting token');
throw new \LogicException('unexisting token');
}
if ($token->hasExpired()) {

View File

@@ -24,11 +24,7 @@ use Chill\CalendarBundle\Entity\Invite;
use Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use DateTimeImmutable;
use DateTimeZone;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -41,44 +37,29 @@ class RemoteEventConverter
* valid when the remote string contains also a timezone, like in
* lastModifiedDate.
*/
public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\\TH:i:s.u?P';
final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\\TH:i:s.u?P';
/**
* Same as above, but sometimes the date is expressed with only 6 milliseconds.
*/
public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\\TH:i:s.uP';
final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\\TH:i:s.uP';
private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0';
private const REMOTE_DATETIME_WITHOUT_TZ_FORMAT = 'Y-m-d\TH:i:s.u?';
private DateTimeZone $defaultDateTimeZone;
private readonly \DateTimeZone $defaultDateTimeZone;
private EngineInterface $engine;
private LocationConverter $locationConverter;
private LoggerInterface $logger;
private PersonRenderInterface $personRender;
private DateTimeZone $remoteDateTimeZone;
private TranslatorInterface $translator;
private readonly \DateTimeZone $remoteDateTimeZone;
public function __construct(
EngineInterface $engine,
LocationConverter $locationConverter,
LoggerInterface $logger,
PersonRenderInterface $personRender,
TranslatorInterface $translator
private readonly \Twig\Environment $engine,
private readonly LocationConverter $locationConverter,
private readonly LoggerInterface $logger,
private readonly PersonRenderInterface $personRender,
private readonly TranslatorInterface $translator
) {
$this->engine = $engine;
$this->locationConverter = $locationConverter;
$this->logger = $logger;
$this->translator = $translator;
$this->personRender = $personRender;
$this->defaultDateTimeZone = (new DateTimeImmutable())->getTimezone();
$this->defaultDateTimeZone = (new \DateTimeImmutable())->getTimezone();
$this->remoteDateTimeZone = self::getRemoteTimeZone();
}
@@ -118,7 +99,7 @@ class RemoteEventConverter
{
$result = array_merge(
[
'subject' => '[Chill] ' .
'subject' => '[Chill] '.
implode(
', ',
$calendar->getPersons()->map(fn (Person $p) => $this->personRender->renderString($p, []))->toArray()
@@ -134,7 +115,7 @@ class RemoteEventConverter
'timeZone' => 'UTC',
],
'allowNewTimeProposals' => false,
'transactionId' => 'calendar_' . $calendar->getId(),
'transactionId' => 'calendar_'.$calendar->getId(),
'body' => [
'contentType' => 'text',
'content' => $this->engine->render(
@@ -167,45 +148,45 @@ class RemoteEventConverter
public function convertAvailabilityToRemoteEvent(array $event): RemoteEvent
{
$startDate =
DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['start']['dateTime'], $this->remoteDateTimeZone)
\DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['start']['dateTime'], $this->remoteDateTimeZone)
->setTimezone($this->defaultDateTimeZone);
$endDate =
DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['end']['dateTime'], $this->remoteDateTimeZone)
\DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['end']['dateTime'], $this->remoteDateTimeZone)
->setTimezone($this->defaultDateTimeZone);
return new RemoteEvent(
uniqid('generated_'),
$this->translator->trans('remote_ms_graph.freebusy_statuses.' . $event['status']),
$this->translator->trans('remote_ms_graph.freebusy_statuses.'.$event['status']),
'',
$startDate,
$endDate
);
}
public static function convertStringDateWithoutTimezone(string $date): DateTimeImmutable
public static function convertStringDateWithoutTimezone(string $date): \DateTimeImmutable
{
$d = DateTimeImmutable::createFromFormat(
$d = \DateTimeImmutable::createFromFormat(
self::REMOTE_DATETIME_WITHOUT_TZ_FORMAT,
$date,
self::getRemoteTimeZone()
);
if (false === $d) {
throw new RuntimeException("could not convert string date to datetime: {$date}");
throw new \RuntimeException("could not convert string date to datetime: {$date}");
}
return $d->setTimezone((new DateTimeImmutable())->getTimezone());
return $d->setTimezone((new \DateTimeImmutable())->getTimezone());
}
public static function convertStringDateWithTimezone(string $date): DateTimeImmutable
public static function convertStringDateWithTimezone(string $date): \DateTimeImmutable
{
$d = DateTimeImmutable::createFromFormat(self::REMOTE_DATETIMEZONE_FORMAT, $date);
$d = \DateTimeImmutable::createFromFormat(self::REMOTE_DATETIMEZONE_FORMAT, $date);
if (false === $d) {
throw new RuntimeException("could not convert string date to datetime: {$date}");
throw new \RuntimeException("could not convert string date to datetime: {$date}");
}
$d->setTimezone((new DateTimeImmutable())->getTimezone());
$d->setTimezone((new \DateTimeImmutable())->getTimezone());
return $d;
}
@@ -213,10 +194,10 @@ class RemoteEventConverter
public function convertToRemote(array $event): RemoteEvent
{
$startDate =
DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['start']['dateTime'], $this->remoteDateTimeZone)
\DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['start']['dateTime'], $this->remoteDateTimeZone)
->setTimezone($this->defaultDateTimeZone);
$endDate =
DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['end']['dateTime'], $this->remoteDateTimeZone)
\DateTimeImmutable::createFromFormat(self::REMOTE_DATE_FORMAT, $event['end']['dateTime'], $this->remoteDateTimeZone)
->setTimezone($this->defaultDateTimeZone);
return new RemoteEvent(
@@ -229,26 +210,22 @@ class RemoteEventConverter
);
}
public function getLastModifiedDate(array $event): DateTimeImmutable
public function getLastModifiedDate(array $event): \DateTimeImmutable
{
$date = DateTimeImmutable::createFromFormat(self::REMOTE_DATETIMEZONE_FORMAT, $event['lastModifiedDateTime']);
$date = \DateTimeImmutable::createFromFormat(self::REMOTE_DATETIMEZONE_FORMAT, $event['lastModifiedDateTime']);
if (false === $date) {
$date = DateTimeImmutable::createFromFormat(self::REMOTE_DATETIMEZONE_FORMAT_ALT, $event['lastModifiedDateTime']);
$date = \DateTimeImmutable::createFromFormat(self::REMOTE_DATETIMEZONE_FORMAT_ALT, $event['lastModifiedDateTime']);
}
if (false === $date) {
$this->logger->error(self::class . ' Could not convert lastModifiedDate', [
$this->logger->error(self::class.' Could not convert lastModifiedDate', [
'actual' => $event['lastModifiedDateTime'],
'format' => self::REMOTE_DATETIMEZONE_FORMAT,
'format_alt' => self::REMOTE_DATETIMEZONE_FORMAT_ALT,
]);
throw new RuntimeException(sprintf(
'could not convert lastModifiedDate: %s, expected format: %s',
$event['lastModifiedDateTime'],
self::REMOTE_DATETIMEZONE_FORMAT . ' and ' . self::REMOTE_DATETIMEZONE_FORMAT_ALT
));
throw new \RuntimeException(sprintf('could not convert lastModifiedDate: %s, expected format: %s', $event['lastModifiedDateTime'], self::REMOTE_DATETIMEZONE_FORMAT.' and '.self::REMOTE_DATETIMEZONE_FORMAT_ALT));
}
return $date;
@@ -262,9 +239,9 @@ class RemoteEventConverter
return 'Y-m-d\TH:i:s';
}
public static function getRemoteTimeZone(): DateTimeZone
public static function getRemoteTimeZone(): \DateTimeZone
{
return new DateTimeZone('UTC');
return new \DateTimeZone('UTC');
}
private function buildInviteToAttendee(Invite $invite): array

View File

@@ -24,29 +24,16 @@ use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteEventConverter;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class CalendarRangeSyncer
{
private EntityManagerInterface $em;
private LoggerInterface $logger;
private HttpClientInterface $machineHttpClient;
/**
* @param MachineHttpClient $machineHttpClient
*/
public function __construct(
EntityManagerInterface $em,
LoggerInterface $logger,
HttpClientInterface $machineHttpClient
) {
$this->em = $em;
$this->logger = $logger;
$this->machineHttpClient = $machineHttpClient;
public function __construct(private readonly EntityManagerInterface $em, private readonly LoggerInterface $logger, private readonly HttpClientInterface $machineHttpClient)
{
}
public function handleCalendarRangeSync(CalendarRange $calendarRange, array $notification, User $user): void
@@ -59,7 +46,7 @@ class CalendarRangeSyncer
}
$calendarRange->preventEnqueueChanges = true;
$this->logger->info(self::class . ' remove a calendar range because deleted on remote calendar');
$this->logger->info(self::class.' remove a calendar range because deleted on remote calendar');
$this->em->remove($calendarRange);
break;
@@ -71,7 +58,7 @@ class CalendarRangeSyncer
$notification['resource']
)->toArray();
} catch (ClientExceptionInterface $clientException) {
$this->logger->warning(self::class . ' could not retrieve event from ms graph. Already deleted ?', [
$this->logger->warning(self::class.' could not retrieve event from ms graph. Already deleted ?', [
'calendarRangeId' => $calendarRange->getId(),
'remoteEventId' => $notification['resource'],
]);
@@ -82,7 +69,7 @@ class CalendarRangeSyncer
$lastModified = RemoteEventConverter::convertStringDateWithTimezone($new['lastModifiedDateTime']);
if ($calendarRange->getRemoteAttributes()['lastModifiedDateTime'] === $lastModified->getTimestamp()) {
$this->logger->info(self::class . ' change key is equals. Source is probably a local update', [
$this->logger->info(self::class.' change key is equals. Source is probably a local update', [
'calendarRangeId' => $calendarRange->getId(),
'remoteEventId' => $notification['resource'],
]);
@@ -104,7 +91,7 @@ class CalendarRangeSyncer
break;
default:
throw new RuntimeException('This changeType is not suppored: ' . $notification['changeType']);
throw new \RuntimeException('This changeType is not suppored: '.$notification['changeType']);
}
}
}

View File

@@ -23,44 +23,23 @@ use Chill\CalendarBundle\Entity\Invite;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteEventConverter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use LogicException;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use function in_array;
class CalendarSyncer
{
private LoggerInterface $logger;
private HttpClientInterface $machineHttpClient;
private UserRepositoryInterface $userRepository;
public function __construct(LoggerInterface $logger, HttpClientInterface $machineHttpClient, UserRepositoryInterface $userRepository)
public function __construct(private readonly LoggerInterface $logger, private readonly HttpClientInterface $machineHttpClient, private readonly UserRepositoryInterface $userRepository)
{
$this->logger = $logger;
$this->machineHttpClient = $machineHttpClient;
$this->userRepository = $userRepository;
}
public function handleCalendarSync(Calendar $calendar, array $notification, User $user): void
{
switch ($notification['changeType']) {
case 'deleted':
$this->handleDeleteCalendar($calendar, $notification, $user);
break;
case 'updated':
$this->handleUpdateCalendar($calendar, $notification, $user);
break;
default:
throw new RuntimeException('this change type is not supported: ' . $notification['changeType']);
}
match ($notification['changeType']) {
'deleted' => $this->handleDeleteCalendar($calendar, $notification, $user),
'updated' => $this->handleUpdateCalendar($calendar, $notification, $user),
default => throw new \RuntimeException('this change type is not supported: '.$notification['changeType']),
};
}
private function handleDeleteCalendar(Calendar $calendar, array $notification, User $user): void
@@ -79,7 +58,7 @@ class CalendarSyncer
$notification['resource']
)->toArray();
} catch (ClientExceptionInterface $clientException) {
$this->logger->warning(self::class . ' could not retrieve event from ms graph. Already deleted ?', [
$this->logger->warning(self::class.' could not retrieve event from ms graph. Already deleted ?', [
'calendarId' => $calendar->getId(),
'remoteEventId' => $notification['resource'],
]);
@@ -96,7 +75,7 @@ class CalendarSyncer
);
if ($calendar->getRemoteAttributes()['lastModifiedDateTime'] === $lastModified->getTimestamp()) {
$this->logger->info(self::class . ' change key is equals. Source is probably a local update', [
$this->logger->info(self::class.' change key is equals. Source is probably a local update', [
'calendarRangeId' => $calendar->getId(),
'remoteEventId' => $notification['resource'],
]);
@@ -137,7 +116,7 @@ class CalendarSyncer
}
$email = $attendee['emailAddress']['address'];
$emails[] = strtolower($email);
$emails[] = strtolower((string) $email);
$user = $this->userRepository->findOneByUsernameOrEmail($email);
if (null === $user) {
@@ -150,38 +129,17 @@ class CalendarSyncer
$invite = $calendar->getInviteForUser($user);
switch ($status) {
// possible cases: none, organizer, tentativelyAccepted, accepted, declined, notResponded.
case 'none':
case 'notResponded':
$invite->setStatus(Invite::PENDING);
break;
case 'tentativelyAccepted':
$invite->setStatus(Invite::TENTATIVELY_ACCEPTED);
break;
case 'accepted':
$invite->setStatus(Invite::ACCEPTED);
break;
case 'declined':
$invite->setStatus(Invite::DECLINED);
break;
default:
throw new LogicException('should not happens, not implemented: ' . $status);
break;
}
match ($status) {
'none', 'notResponded' => $invite->setStatus(Invite::PENDING),
'tentativelyAccepted' => $invite->setStatus(Invite::TENTATIVELY_ACCEPTED),
'accepted' => $invite->setStatus(Invite::ACCEPTED),
'declined' => $invite->setStatus(Invite::DECLINED),
default => throw new \LogicException('should not happens, not implemented: '.$status),
};
}
foreach ($calendar->getUsers() as $user) {
if (!in_array(strtolower($user->getEmailCanonical()), $emails, true)) {
if (!\in_array(strtolower($user->getEmailCanonical()), $emails, true)) {
$calendar->removeUser($user);
}
}

View File

@@ -28,8 +28,6 @@ use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteEventConverter;
use Chill\CalendarBundle\Repository\CalendarRangeRepository;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use Exception;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
@@ -38,62 +36,16 @@ use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
use function count;
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
{
private array $cacheScheduleTimeForUser = [];
private CalendarRangeRepository $calendarRangeRepository;
private CalendarRepository $calendarRepository;
private LoggerInterface $logger;
private HttpClientInterface $machineHttpClient;
private MapCalendarToUser $mapCalendarToUser;
private RemoteEventConverter $remoteEventConverter;
private OnBehalfOfUserTokenStorage $tokenStorage;
private TranslatorInterface $translator;
private UrlGeneratorInterface $urlGenerator;
private OnBehalfOfUserHttpClient $userHttpClient;
private Security $security;
public function __construct(
CalendarRepository $calendarRepository,
CalendarRangeRepository $calendarRangeRepository,
HttpClientInterface $machineHttpClient,
MapCalendarToUser $mapCalendarToUser,
LoggerInterface $logger,
OnBehalfOfUserTokenStorage $tokenStorage,
OnBehalfOfUserHttpClient $userHttpClient,
RemoteEventConverter $remoteEventConverter,
TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator,
Security $security
) {
$this->calendarRepository = $calendarRepository;
$this->calendarRangeRepository = $calendarRangeRepository;
$this->machineHttpClient = $machineHttpClient;
$this->mapCalendarToUser = $mapCalendarToUser;
$this->logger = $logger;
$this->remoteEventConverter = $remoteEventConverter;
$this->tokenStorage = $tokenStorage;
$this->translator = $translator;
$this->urlGenerator = $urlGenerator;
$this->userHttpClient = $userHttpClient;
$this->security = $security;
public function __construct(private readonly CalendarRepository $calendarRepository, private readonly CalendarRangeRepository $calendarRangeRepository, private readonly HttpClientInterface $machineHttpClient, private readonly MapCalendarToUser $mapCalendarToUser, private readonly LoggerInterface $logger, private readonly OnBehalfOfUserTokenStorage $tokenStorage, private readonly OnBehalfOfUserHttpClient $userHttpClient, private readonly RemoteEventConverter $remoteEventConverter, private readonly TranslatorInterface $translator, private readonly UrlGeneratorInterface $urlGenerator, private readonly Security $security)
{
}
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int
public function countEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int
{
$userId = $this->mapCalendarToUser->getUserId($user);
@@ -104,7 +56,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$data = $this->userHttpClient->request(
'GET',
'users/' . $userId . '/calendarView',
'users/'.$userId.'/calendarView',
[
'query' => [
'startDateTime' => $startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
@@ -116,7 +68,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
)->toArray();
} catch (ClientExceptionInterface $e) {
if (403 === $e->getResponse()->getStatusCode()) {
return count($this->getScheduleTimesForUser($user, $startDate, $endDate));
return \count($this->getScheduleTimesForUser($user, $startDate, $endDate));
}
$this->logger->error('Could not get list of event on MSGraph', [
@@ -153,6 +105,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$this->logger->debug('mark user ready for msgraph calendar as he does not have any mapping', [
'userId' => $user->getId(),
]);
return true;
}
@@ -160,14 +113,14 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
}
/**
* @return array|\Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent[]
*
* @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
*
* @return array|\Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent[]
*/
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array
public function listEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array
{
$userId = $this->mapCalendarToUser->getUserId($user);
@@ -178,7 +131,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$bareEvents = $this->userHttpClient->request(
'GET',
'users/' . $userId . '/calendarView',
'users/'.$userId.'/calendarView',
[
'query' => [
'startDateTime' => $startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
@@ -268,7 +221,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$calendar->getRemoteId(),
$this->translator->trans('remote_ms_graph.cancel_event_because_main_user_is_%label%', ['%label%' => $calendar->getMainUser()]),
$previousMainUser,
'calendar_' . $calendar->getRemoteId()
'calendar_'.$calendar->getRemoteId()
);
$this->createCalendarOnRemote($calendar);
} else {
@@ -346,7 +299,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
break;
default:
throw new Exception('not supported');
throw new \Exception('not supported');
}
try {
@@ -359,7 +312,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$this->logger->warning('could not update calendar range to remote', [
'exception' => $e->getTraceAsString(),
'content' => $e->getResponse()->getContent(),
'calendarRangeId' => 'invite_' . $invite->getId(),
'calendarRangeId' => 'invite_'.$invite->getId(),
]);
throw $e;
@@ -401,7 +354,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
'id' => $id,
'lastModifiedDateTime' => $lastModified,
'changeKey' => $changeKey
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_' . $calendar->getId());
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_'.$calendar->getId());
if (null === $id) {
return;
@@ -436,7 +389,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$event = $this->machineHttpClient->request(
'POST',
'users/' . $userId . '/calendar/events',
'users/'.$userId.'/calendar/events',
[
'json' => $eventData,
]
@@ -480,7 +433,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
] = $this->createOnRemote(
$eventData,
$calendarRange->getUser(),
'calendar_range_' . $calendarRange->getId()
'calendar_range_'.$calendarRange->getId()
);
$calendarRange->setRemoteId($id)
@@ -503,7 +456,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$userId = $this->mapCalendarToUser->getUserId($user);
if ('' === $iCalUid = ($event['iCalUId'] ?? '')) {
throw new Exception('no iCalUid for this event');
throw new \Exception('no iCalUid for this event');
}
try {
@@ -521,8 +474,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
throw $clientException;
}
if (1 !== count($events['value'])) {
throw new Exception('multiple events found with same iCalUid');
if (1 !== \count($events['value'])) {
throw new \Exception('multiple events found with same iCalUid');
}
return $events['value'][0]['id'];
@@ -533,19 +486,13 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$userId = $this->mapCalendarToUser->getUserId($user);
if (null === $userId) {
throw new Exception(
sprintf(
'no remote calendar for this user: %s, remoteid: %s',
$user->getId(),
$remoteId
)
);
throw new \Exception(sprintf('no remote calendar for this user: %s, remoteid: %s', $user->getId(), $remoteId));
}
try {
return $this->machineHttpClient->request(
'GET',
'users/' . $userId . '/calendar/events/' . $remoteId
'users/'.$userId.'/calendar/events/'.$remoteId
)->toArray();
} catch (ClientExceptionInterface $e) {
$this->logger->warning('Could not get event from calendar', [
@@ -556,11 +503,11 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
}
}
private function getScheduleTimesForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): array
private function getScheduleTimesForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): array
{
$userId = $this->mapCalendarToUser->getUserId($user);
if (array_key_exists($userId, $this->cacheScheduleTimeForUser)) {
if (\array_key_exists($userId, $this->cacheScheduleTimeForUser)) {
return $this->cacheScheduleTimeForUser[$userId];
}
@@ -575,17 +522,17 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$body = [
'schedules' => [$user->getEmailCanonical()],
'startTime' => [
'dateTime' => ($startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat())),
'dateTime' => $startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
'timeZone' => 'UTC',
],
'endTime' => [
'dateTime' => ($endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat())),
'dateTime' => $endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
'timeZone' => 'UTC',
],
];
try {
$response = $this->userHttpClient->request('POST', 'users/' . $userId . '/calendar/getSchedule', [
$response = $this->userHttpClient->request('POST', 'users/'.$userId.'/calendar/getSchedule', [
'json' => $body,
])->toArray();
} catch (ClientExceptionInterface $e) {
@@ -610,7 +557,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$eventDatas = [];
$eventDatas[] = $this->remoteEventConverter->calendarToEvent($calendar);
if (0 < count($newInvites)) {
if (0 < \count($newInvites)) {
// it seems that invitaiton are always send, even if attendee changes are mixed with other datas
// $eventDatas[] = $this->remoteEventConverter->calendarToEventAttendeesOnly($calendar);
}
@@ -624,7 +571,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$calendar->getRemoteId(),
$eventData,
$calendar->getMainUser(),
'calendar_' . $calendar->getId()
'calendar_'.$calendar->getId()
);
$calendar->addRemoteAttributes([
@@ -655,7 +602,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$event = $this->machineHttpClient->request(
'PATCH',
'users/' . $userId . '/calendar/events/' . $remoteId,
'users/'.$userId.'/calendar/events/'.$remoteId,
[
'json' => $eventData,
]
@@ -683,9 +630,9 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$this->machineHttpClient->request(
'DELETE',
'users/' . $userId . '/calendar/events/' . $remoteId
'users/'.$userId.'/calendar/events/'.$remoteId
);
} catch (ClientExceptionInterface $e) {
} catch (ClientExceptionInterface) {
$this->logger->warning('could not remove event from calendar', [
'event_remote_id' => $remoteId,
'user_id' => $user->getId(),
@@ -710,7 +657,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$event = $this->machineHttpClient->request(
'GET',
'users/' . $userId . '/calendar/events/' . $calendarRange->getRemoteId()
'users/'.$userId.'/calendar/events/'.$calendarRange->getRemoteId()
)->toArray();
} catch (ClientExceptionInterface $e) {
$this->logger->warning('Could not get event from calendar', [
@@ -737,7 +684,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
try {
$event = $this->machineHttpClient->request(
'PATCH',
'users/' . $userId . '/calendar/events/' . $calendarRange->getRemoteId(),
'users/'.$userId.'/calendar/events/'.$calendarRange->getRemoteId(),
[
'json' => $eventData,
]

View File

@@ -22,20 +22,18 @@ use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\CalendarBundle\Entity\Invite;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use LogicException;
use Symfony\Component\HttpFoundation\Response;
class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface
{
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int
public function countEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int
{
return 0;
}
public function getMakeReadyResponse(string $returnPath): Response
{
throw new LogicException('As this connector is always ready, this method should not be called');
throw new \LogicException('As this connector is always ready, this method should not be called');
}
public function isReady(): bool
@@ -43,7 +41,7 @@ class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface
return true;
}
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array
public function listEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array
{
return [];
}

View File

@@ -23,12 +23,11 @@ use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\CalendarBundle\Entity\Invite;
use Chill\CalendarBundle\RemoteCalendar\Model\RemoteEvent;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use Symfony\Component\HttpFoundation\Response;
interface RemoteCalendarConnectorInterface
{
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int;
public function countEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int;
/**
* Return a response, more probably a RedirectResponse, where the user
@@ -46,7 +45,7 @@ interface RemoteCalendarConnectorInterface
/**
* @return array|RemoteEvent[]
*/
public function listEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array;
public function listEventsForUser(User $user, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate, ?int $offset = 0, ?int $limit = 50): array;
public function removeCalendar(string $remoteId, array $remoteAttributes, User $user, ?CalendarRange $associatedCalendarRange = null): void;

View File

@@ -28,7 +28,6 @@ use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSUserAbsenceSync;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraphRemoteCalendarConnector;
use Chill\CalendarBundle\RemoteCalendar\Connector\NullRemoteCalendarConnector;
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
use RuntimeException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -43,7 +42,7 @@ class RemoteCalendarCompilerPass implements CompilerPassInterface
if (true === $config['remote_calendars_sync']['microsoft_graph']['enabled']) {
$connector = MSGraphRemoteCalendarConnector::class;
$container->setAlias(HttpClientInterface::class . ' $machineHttpClient', MachineHttpClient::class);
$container->setAlias(HttpClientInterface::class.' $machineHttpClient', MachineHttpClient::class);
} else {
$connector = NullRemoteCalendarConnector::class;
// remove services which cannot be loaded

View File

@@ -18,45 +18,32 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\RemoteCalendar\Model;
use DateTimeImmutable;
use Symfony\Component\Serializer\Annotation as Serializer;
class RemoteEvent
{
public string $description;
/**
* @Serializer\Groups({"read"})
*/
public DateTimeImmutable $endDate;
/**
* @Serializer\Groups({"read"})
*/
public string $id;
/**
* @Serializer\Groups({"read"})
*/
public bool $isAllDay;
/**
* @Serializer\Groups({"read"})
*/
public DateTimeImmutable $startDate;
/**
* @Serializer\Groups({"read"})
*/
public string $title;
public function __construct(string $id, string $title, string $description, DateTimeImmutable $startDate, DateTimeImmutable $endDate, bool $isAllDay = false)
{
$this->id = $id;
$this->title = $title;
$this->description = $description;
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->isAllDay = $isAllDay;
public function __construct(
/**
* @Serializer\Groups({"read"})
*/
public string $id,
/**
* @Serializer\Groups({"read"})
*/
public string $title,
public string $description,
/**
* @Serializer\Groups({"read"})
*/
public \DateTimeImmutable $startDate,
/**
* @Serializer\Groups({"read"})
*/
public \DateTimeImmutable $endDate,
/**
* @Serializer\Groups({"read"})
*/
public bool $isAllDay = false
) {
}
}

View File

@@ -23,25 +23,16 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepositoryInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
{
private AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository;
private EntityManagerInterface $em;
public function __construct(
AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository,
EntityManagerInterface $em
) {
$this->accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository;
$this->em = $em;
public function __construct(private readonly AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository, private readonly EntityManagerInterface $em)
{
}
public function buildQueryByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
public function buildQueryByAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): QueryBuilder
{
$qb = $this->em->createQueryBuilder();
$qb->from(Calendar::class, 'c');
@@ -64,7 +55,7 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb;
}
public function buildQueryByAccompanyingPeriodIgnoredByDates(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
public function buildQueryByAccompanyingPeriodIgnoredByDates(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): QueryBuilder
{
$qb = $this->em->createQueryBuilder();
$qb->from(Calendar::class, 'c');
@@ -90,7 +81,7 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
/**
* Base implementation. The list of allowed accompanying period is retrieved "manually" from @see{AccompanyingPeriodACLAwareRepository}.
*/
public function buildQueryByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
public function buildQueryByPerson(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): QueryBuilder
{
$qb = $this->em->createQueryBuilder()
->from(Calendar::class, 'c');
@@ -114,7 +105,7 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
/**
* Base implementation. The list of allowed accompanying period is retrieved "manually" from @see{AccompanyingPeriodACLAwareRepository}.
*/
public function buildQueryByPersonIgnoredByDates(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
public function buildQueryByPersonIgnoredByDates(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): QueryBuilder
{
$qb = $this->em->createQueryBuilder()
->from(Calendar::class, 'c');
@@ -135,14 +126,14 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb;
}
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): int
{
$qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('count(c)');
return $qb->getQuery()->getSingleScalarResult();
}
public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
public function countByPerson(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): int
{
return $this->buildQueryByPerson($person, $startDate, $endDate)
->select('COUNT(c)')
@@ -150,14 +141,14 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
->getSingleScalarResult();
}
public function countIgnoredByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
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
public function countIgnoredByPerson(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): int
{
return $this->buildQueryByPersonIgnoredByDates($person, $startDate, $endDate)
->select('COUNT(c)')
@@ -168,12 +159,12 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
/**
* @return array|Calendar[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array
public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array
{
$qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('c');
foreach ($orderBy as $sort => $order) {
$qb->addOrderBy('c.' . $sort, $order);
$qb->addOrderBy('c.'.$sort, $order);
}
if (null !== $offset) {
@@ -187,13 +178,13 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb->getQuery()->getResult();
}
public function findByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array
public function findByPerson(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array
{
$qb = $this->buildQueryByPerson($person, $startDate, $endDate)
->select('c');
foreach ($orderBy as $sort => $order) {
$qb->addOrderBy('c.' . $sort, $order);
$qb->addOrderBy('c.'.$sort, $order);
}
if (null !== $offset) {

View File

@@ -21,33 +21,32 @@ namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
interface CalendarACLAwareRepositoryInterface
{
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): int;
/**
* Return the number or calendars associated with a person. See condition on @see{self::findByPerson}.
*/
public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
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;
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;
public function countIgnoredByPerson(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate): int;
/**
* @return array|Calendar[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array;
public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array;
/**
* Return all the calendars which are associated with a person, either on @see{Calendar::person} or within.
@@ -59,5 +58,5 @@ interface CalendarACLAwareRepositoryInterface
*
* @return array|Calendar[]
*/
public function findByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array;
public function findByPerson(Person $person, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array;
}

View File

@@ -18,7 +18,7 @@ use Doctrine\Persistence\ObjectRepository;
class CalendarDocRepository implements ObjectRepository, CalendarDocRepositoryInterface
{
private EntityRepository $repository;
private readonly EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -13,28 +13,23 @@ namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
use function count;
class CalendarRangeRepository implements ObjectRepository
{
private EntityManagerInterface $em;
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
public function __construct(private readonly EntityManagerInterface $em)
{
$this->em = $entityManager;
$this->repository = $entityManager->getRepository(CalendarRange::class);
$this->repository = $em->getRepository(CalendarRange::class);
}
public function countByAvailableRangesForUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): int
public function countByAvailableRangesForUser(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): int
{
return $this->buildQueryAvailableRangesForUser($user, $from, $to)
->select('COUNT(cr)')
@@ -67,8 +62,8 @@ class CalendarRangeRepository implements ObjectRepository
*/
public function findByAvailableRangesForUser(
User $user,
DateTimeImmutable $from,
DateTimeImmutable $to,
\DateTimeImmutable $from,
\DateTimeImmutable $to,
?int $limit = null,
?int $offset = null
): array {
@@ -101,7 +96,7 @@ class CalendarRangeRepository implements ObjectRepository
*/
public function findRemoteIdsPresent(array $remoteIds): array
{
if (0 === count($remoteIds)) {
if (0 === \count($remoteIds)) {
return [];
}
@@ -116,7 +111,7 @@ class CalendarRangeRepository implements ObjectRepository
$remoteIdsStr = implode(
', ',
array_fill(0, count($remoteIds), '((?))')
array_fill(0, \count($remoteIds), '((?))')
);
$rsm = new ResultSetMapping();
@@ -146,7 +141,7 @@ class CalendarRangeRepository implements ObjectRepository
return CalendarRange::class;
}
private function buildQueryAvailableRangesForUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): QueryBuilder
private function buildQueryAvailableRangesForUser(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('cr');

View File

@@ -14,20 +14,18 @@ namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use DateTimeImmutable;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
use function count;
class CalendarRepository implements ObjectRepository
{
private EntityManagerInterface $em;
private readonly EntityManagerInterface $em;
private EntityRepository $repository;
private readonly EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
@@ -40,7 +38,7 @@ class CalendarRepository implements ObjectRepository
return $this->repository->count(['accompanyingPeriod' => $period]);
}
public function countByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): int
public function countByUser(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): int
{
return $this->buildQueryByUser($user, $from, $to)
->select('COUNT(c)')
@@ -89,7 +87,7 @@ class CalendarRepository implements ObjectRepository
);
}
public function findByNotificationAvailable(DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $limit = null, ?int $offset = null): array
public function findByNotificationAvailable(\DateTimeImmutable $startDate, \DateTimeImmutable $endDate, ?int $limit = null, ?int $offset = null): array
{
$qb = $this->queryByNotificationAvailable($startDate, $endDate)->select('c');
@@ -107,7 +105,7 @@ class CalendarRepository implements ObjectRepository
/**
* @return array|Calendar[]
*/
public function findByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to, ?int $limit = null, ?int $offset = null): array
public function findByUser(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to, ?int $limit = null, ?int $offset = null): array
{
$qb = $this->buildQueryByUser($user, $from, $to)->select('c');
@@ -138,13 +136,13 @@ class CalendarRepository implements ObjectRepository
*/
public function findRemoteIdsPresent(array $remoteIds): array
{
if (0 === count($remoteIds)) {
if (0 === \count($remoteIds)) {
return [];
}
$remoteIdsStr = implode(
', ',
array_fill(0, count($remoteIds), '((?))')
array_fill(0, \count($remoteIds), '((?))')
);
$sql = "SELECT
@@ -183,7 +181,7 @@ class CalendarRepository implements ObjectRepository
return Calendar::class;
}
private function buildQueryByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): QueryBuilder
private function buildQueryByUser(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('c');
@@ -202,7 +200,7 @@ class CalendarRepository implements ObjectRepository
]);
}
private function queryByNotificationAvailable(DateTimeImmutable $startDate, DateTimeImmutable $endDate): QueryBuilder
private function queryByNotificationAvailable(\DateTimeImmutable $startDate, \DateTimeImmutable $endDate): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('c');

View File

@@ -18,7 +18,7 @@ use Doctrine\Persistence\ObjectRepository;
class InviteRepository implements ObjectRepository
{
private EntityRepository $entityRepository;
private readonly EntityRepository $entityRepository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -1,13 +1,10 @@
---
services:
Chill\CalendarBundle\DataFixtures\ORM\LoadCancelReason:
tags:
- { 'name': doctrine.fixture.orm }
Chill\CalendarBundle\DataFixtures\ORM\LoadInvite:
tags:
- { 'name': doctrine.fixture.orm }
Chill\CalendarBundle\DataFixtures\ORM\LoadCalendarRange:
autowire: true
autoconfigure: true
tags:
- { 'name': doctrine.fixture.orm }
_defaults:
autowire: true
autoconfigure: true
Chill\CalendarBundle\DataFixtures\ORM\:
resource: './../../../DataFixtures/ORM'
tags:
- { 'name': doctrine.fixture.orm }

View File

@@ -69,7 +69,7 @@
or calendar.users|length > 0 %}
<div class="item-row details separator">
<div class="item-col">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {
{% include '@ChillActivity/Activity/concernedGroups.html.twig' with {
'context': calendar.context == 'person' ? 'calendar_person' : 'calendar_accompanyingCourse',
'render': 'wrap-list',
'entity': calendar

View File

@@ -7,7 +7,7 @@
{% block content %}
<div class="calendar-edit">
{% include 'ChillCalendarBundle:Calendar:edit.html.twig' with {'context': 'accompanyingCourse'} %}
{% include '@ChillCalendar/Calendar/edit.html.twig' with {'context': 'accompanyingCourse'} %}
</div>
{% endblock %}

View File

@@ -7,7 +7,7 @@
{% block content %}
<div class="calendar-edit">
{% include 'ChillCalendarBundle:Calendar:edit.html.twig' with {'context': 'person'} %}
{% include '@ChillCalendar/Calendar/edit.html.twig' with {'context': 'person'} %}
</div>
{% endblock %}

View File

@@ -6,7 +6,7 @@
<div class="calendar-edit">
<div id="calendar"></div> {# <=== vue component #}
{% include 'ChillCalendarBundle:Calendar:edit.html.twig' with {'context': 'user'} %}
{% include '@ChillCalendar/Calendar/edit.html.twig' with {'context': 'user'} %}
</div>
{% endblock %}

View File

@@ -8,7 +8,7 @@
<div class="calendar-new">
<div id="calendar"></div> {# <=== vue component #}
{% include 'ChillCalendarBundle:Calendar:new.html.twig' with {'context': 'accompanyingCourse'} %}
{% include '@ChillCalendar/Calendar/new.html.twig' with {'context': 'accompanyingCourse'} %}
</div>
{% endblock %}

View File

@@ -8,7 +8,7 @@
<div class="calendar-new">
<div id="calendar"></div> {# <=== vue component #}
{% include 'ChillCalendarBundle:Calendar:new.html.twig' with {'context': 'person'} %}
{% include '@ChillCalendar/Calendar/new.html.twig' with {'context': 'person'} %}
</div>
{% endblock %}

View File

@@ -15,7 +15,7 @@
</dl>
<h2 class="chill-red">{{ 'Concerned groups calendar'|trans }}</h2>
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': 'calendar_' ~ context, 'render': 'bloc' } %}
{% include '@ChillActivity/Activity/concernedGroups.html.twig' with {'context': 'calendar_' ~ context, 'render': 'bloc' } %}
<h2 class="chill-red">{{ 'Calendar data'|trans }}</h2>

View File

@@ -7,7 +7,7 @@
{% block content -%}
<div class="calendar-show">
{% include 'ChillCalendarBundle:Calendar:show.html.twig' with {'context': 'accompanyingCourse'} %}
{% include '@ChillCalendar/Calendar/show.html.twig' with {'context': 'accompanyingCourse'} %}
</div>
{% endblock content %}

View File

@@ -5,7 +5,7 @@
{% block content -%}
<div class="calendar-show">
{% include 'ChillCalendarBundle:Calendar:show.html.twig' with {'context': 'user'} %}
{% include '@ChillCalendar/Calendar/show.html.twig' with {'context': 'user'} %}
</div>
{% endblock content %}

View File

@@ -15,47 +15,36 @@ 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';
final public const EDIT = 'CHILL_CALENDAR_DOC_EDIT';
public const SEE = 'CHILL_CALENDAR_DOC_SEE';
final 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)
public function __construct(private readonly Security $security)
{
$this->security = $security;
}
protected function supports($attribute, $subject): bool
{
return in_array($attribute, self::ALL, true) && $subject instanceof CalendarDoc;
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);
}
return match ($attribute) {
self::EDIT => $this->security->isGranted(CalendarVoter::EDIT, $subject->getCalendar()),
self::SEE => $this->security->isGranted(CalendarVoter::SEE, $subject->getCalendar()),
default => throw new \UnexpectedValueException('Attribute not supported: '.$attribute),
};
}
}

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