diff --git a/config/services.yaml b/config/services.yaml index 296f22d381d..ca8d11d56a2 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -121,6 +121,8 @@ services: tags: ['app.acquisition_statistics.calculator'] App\Adherent\Tag\TagGenerator\TagGeneratorInterface: tags: ['app.adherent.tag.generator'] + App\JeMengage\Timeline\FeedProcessor\FeedProcessorInterface: + tags: ['app.timeline.feed_processor'] # Imports App\: @@ -189,6 +191,10 @@ services: arguments: $senders: !tagged_iterator 'app.adherent_message.sender' + App\JeMengage\Timeline\DataProvider: + arguments: + $processors: !tagged_iterator 'app.timeline.feed_processor' + App\Adherent\Tag\TagAggregator: arguments: $generators: !tagged_iterator {tag: 'app.adherent.tag.generator'} diff --git a/config/settings/algolia_search/jemengage_timeline_feed-settings.json b/config/settings/algolia_search/jemengage_timeline_feed-settings.json index 3f036995240..4123af1448b 100644 --- a/config/settings/algolia_search/jemengage_timeline_feed-settings.json +++ b/config/settings/algolia_search/jemengage_timeline_feed-settings.json @@ -28,7 +28,8 @@ "time_zone", "title", "type", - "url" + "url", + "visibility" ], "unretrievableAttributes": [], "optionalWords": null, diff --git a/src/Controller/Api/JeMengage/GetTimelineFeedsController.php b/src/Controller/Api/JeMengage/GetTimelineFeedsController.php index 483ebef2274..23a6355b407 100644 --- a/src/Controller/Api/JeMengage/GetTimelineFeedsController.php +++ b/src/Controller/Api/JeMengage/GetTimelineFeedsController.php @@ -2,10 +2,9 @@ namespace App\Controller\Api\JeMengage; -use App\Algolia\SearchService; use App\Entity\Adherent; -use App\Entity\Algolia\AlgoliaJeMengageTimelineFeed; use App\Intl\FranceCitiesBundle; +use App\JeMengage\Timeline\DataProvider; use App\JeMengage\Timeline\TimelineFeedTypeEnum; use App\OAuth\Model\DeviceApiUser; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; @@ -19,7 +18,7 @@ #[IsGranted('ROLE_OAUTH_SCOPE_JEMARCHE_APP')] class GetTimelineFeedsController extends AbstractController { - public function __invoke(Request $request, SearchService $searchService): JsonResponse + public function __invoke(Request $request, DataProvider $dataProvider): JsonResponse { /** @var Adherent|DeviceApiUser $user */ $user = $this->getUser(); @@ -59,12 +58,7 @@ public function __invoke(Request $request, SearchService $searchService): JsonRe $filters[] = 'adherent_ids:'.$user->getId(); } - $timelineFeeds = $searchService->rawSearch(AlgoliaJeMengageTimelineFeed::class, '', [ - 'page' => $page, - 'attributesToHighlight' => [], - 'filters' => implode(' OR ', $filters), - 'tagFilters' => $tagFilters, - ]); + $timelineFeeds = $dataProvider->findItems($user, $page, $filters, $tagFilters); return $this->json($timelineFeeds, Response::HTTP_OK); } diff --git a/src/Entity/Event/BaseEvent.php b/src/Entity/Event/BaseEvent.php index 2040950bc94..2362867026e 100644 --- a/src/Entity/Event/BaseEvent.php +++ b/src/Entity/Event/BaseEvent.php @@ -827,7 +827,7 @@ public function setElectoral(bool $electoral): void public function isIndexable(): bool { - return $this->isPublished() && $this->isGeocoded() && AddressInterface::FRANCE === $this->getCountry(); + return $this->isPublished() && !$this->isCancelled(); } public function getIndexOptions(): array diff --git a/src/Event/EventCleaner.php b/src/Event/EventCleaner.php new file mode 100644 index 00000000000..3e962a1016f --- /dev/null +++ b/src/Event/EventCleaner.php @@ -0,0 +1,17 @@ + $value) { + if (!\in_array($key, ['name', 'title', 'objectID', 'uuid', 'slug', 'time_zone', 'begin_at', 'finish_at', 'status', 'visibility', 'image', 'image_url', 'link', 'category'])) { + $eventData[$key] = null; + } + } + + return $eventData; + } +} diff --git a/src/Event/EventVisibilityEnum.php b/src/Event/EventVisibilityEnum.php index d348bfff274..b9e423f3fd6 100644 --- a/src/Event/EventVisibilityEnum.php +++ b/src/Event/EventVisibilityEnum.php @@ -8,4 +8,9 @@ enum EventVisibilityEnum: string case PRIVATE = 'private'; case ADHERENT = 'adherent'; case ADHERENT_DUES = 'adherent_dues'; + + public static function isForAdherent(string $visibility): bool + { + return \in_array($visibility, [self::ADHERENT->value, self::ADHERENT_DUES->value]); + } } diff --git a/src/JeMengage/Timeline/DataProvider.php b/src/JeMengage/Timeline/DataProvider.php new file mode 100644 index 00000000000..ffd92ed784f --- /dev/null +++ b/src/JeMengage/Timeline/DataProvider.php @@ -0,0 +1,51 @@ +search->rawSearch(AlgoliaJeMengageTimelineFeed::class, '', [ + 'page' => $page, + 'attributesToHighlight' => [], + 'filters' => implode(' OR ', $filters), + 'tagFilters' => $tagFilters, + ]); + + $timelineFeeds['hits'] = $this->processItems($user, $timelineFeeds['hits']); + + return $timelineFeeds; + } + + private function processItems(Adherent $user, array $items): array + { + $context = []; + foreach ($items as &$item) { + $item = $this->getProcessor($item)->process($item, $context); + } + + return $items; + } + + private function getProcessor(array $item): FeedProcessorInterface + { + /** @var FeedProcessorInterface $processor */ + foreach ($this->processors as $processor) { + if ($processor->supports($item)) { + return $processor; + } + } + } +} diff --git a/src/JeMengage/Timeline/FeedProcessor/AbstractFeedProcessor.php b/src/JeMengage/Timeline/FeedProcessor/AbstractFeedProcessor.php new file mode 100644 index 00000000000..f67b9203c18 --- /dev/null +++ b/src/JeMengage/Timeline/FeedProcessor/AbstractFeedProcessor.php @@ -0,0 +1,11 @@ +cleanEventDataIfNeed($item, $context); + } + + public function supports(array $item): bool + { + return ($item['type'] ?? null) === TimelineFeedTypeEnum::EVENT; + } + + private function cleanEventDataIfNeed(array $item, array &$context): array + { + $needClean = $context[self::CONTEXT_CLEANER_ENABLED_KEY] ?? null; + + if (null === $needClean) { + $user = $this->security->getUser(); + $needClean = $context[self::CONTEXT_CLEANER_ENABLED_KEY] = + EventVisibilityEnum::isForAdherent($visibility = $item['visibility'] ?? EventVisibilityEnum::ADHERENT_DUES->value) + && ( + !$user instanceof Adherent + || (EventVisibilityEnum::ADHERENT->value === $visibility && !$user->hasTag(TagEnum::ADHERENT)) + || (EventVisibilityEnum::ADHERENT_DUES->value === $visibility && !$user->hasTag(TagEnum::getAdherentYearTag())) + ); + } + + if ($needClean) { + $item = $this->eventCleaner->cleanEventData($item); + $item['object_state'] = 'partial'; + } else { + $item['object_state'] = 'full'; + } + + return $item; + } +} diff --git a/src/JeMengage/Timeline/FeedProcessor/FeedProcessorInterface.php b/src/JeMengage/Timeline/FeedProcessor/FeedProcessorInterface.php new file mode 100644 index 00000000000..dc875390a88 --- /dev/null +++ b/src/JeMengage/Timeline/FeedProcessor/FeedProcessorInterface.php @@ -0,0 +1,10 @@ + $value) { - if (!\in_array($key, ['name', 'uuid', 'slug', 'time_zone', 'begin_at', 'finish_at', 'status', 'visibility', 'image_url', 'link'])) { - $event[$key] = null; - } - } - - return $event; - } - private function cleanEventDataIfNeed(BaseEvent $event, ?Adherent $adherent, array $eventData, string $apiContext): array { if (EventContextBuilder::CONTEXT_PRIVATE === $apiContext) { @@ -89,7 +80,7 @@ private function cleanEventDataIfNeed(BaseEvent $event, ?Adherent $adherent, arr ); if ($needClean) { - $eventData = $this->cleanPrivateEvent($eventData); + $eventData = $this->eventCleaner->cleanEventData($eventData); $eventData['object_state'] = 'partial'; } else { $eventData['object_state'] = 'full'; diff --git a/src/Normalizer/Indexer/AbstractJeMengageTimelineFeedNormalizer.php b/src/Normalizer/Indexer/AbstractJeMengageTimelineFeedNormalizer.php index 330198b7d5f..fca694f7e46 100644 --- a/src/Normalizer/Indexer/AbstractJeMengageTimelineFeedNormalizer.php +++ b/src/Normalizer/Indexer/AbstractJeMengageTimelineFeedNormalizer.php @@ -31,6 +31,7 @@ final public function normalize($object, $format = null, array $context = []) 'adherent_ids' => $this->getAdherentIds($object), 'deeplink' => $this->getDeepLink($object), 'mode' => $this->getMode($object), + 'visibility' => $this->getVisibility($object), 'cta_label' => $this->getCtaLabel($object), 'cta_link' => $this->getCtaLink($object), '_tags' => [$this->getType()], @@ -127,6 +128,11 @@ protected function getCtaLink(object $object): ?string return null; } + protected function getVisibility(object $object): ?string + { + return null; + } + protected function getMediaType(object $object): ?string { return null; diff --git a/src/Normalizer/Indexer/EventNormalizer.php b/src/Normalizer/Indexer/EventNormalizer.php index 51ea7b3dc32..a76e2570bb4 100644 --- a/src/Normalizer/Indexer/EventNormalizer.php +++ b/src/Normalizer/Indexer/EventNormalizer.php @@ -8,11 +8,8 @@ class EventNormalizer extends AbstractJeMengageTimelineFeedNormalizer { - private UrlGeneratorInterface $urlGenerator; - - public function __construct(UrlGeneratorInterface $urlGenerator) + public function __construct(private readonly UrlGeneratorInterface $urlGenerator) { - $this->urlGenerator = $urlGenerator; } protected function getClassName(): string @@ -80,6 +77,12 @@ protected function getMode(object $object): ?string return $object->getMode(); } + /** @param BaseEvent $object */ + protected function getVisibility(object $object): ?string + { + return $object->visibility->value; + } + /** @param BaseEvent $object */ protected function getFinishAt(object $object): ?\DateTime {