![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/app/bundles/NotificationBundle/EventListener/ |
<?php namespace Mautic\NotificationBundle\EventListener; use Mautic\CampaignBundle\CampaignEvents; use Mautic\CampaignBundle\Entity\LeadEventLog; use Mautic\CampaignBundle\Event\CampaignBuilderEvent; use Mautic\CampaignBundle\Event\PendingEvent; use Mautic\CoreBundle\Event\TokenReplacementEvent; use Mautic\LeadBundle\Entity\DoNotContact; use Mautic\LeadBundle\Entity\Lead; use Mautic\LeadBundle\Model\DoNotContact as DoNotContactModel; use Mautic\NotificationBundle\Api\AbstractNotificationApi; use Mautic\NotificationBundle\Entity\Notification; use Mautic\NotificationBundle\Event\NotificationSendEvent; use Mautic\NotificationBundle\Form\Type\MobileNotificationSendType; use Mautic\NotificationBundle\Form\Type\NotificationSendType; use Mautic\NotificationBundle\Model\NotificationModel; use Mautic\NotificationBundle\NotificationEvents; use Mautic\PluginBundle\Helper\IntegrationHelper; use Psr\Http\Message\ResponseInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Contracts\Translation\TranslatorInterface; class CampaignSubscriber implements EventSubscriberInterface { /** * @var string */ public const EVENT_ACTION_SEND_MOBILE_NOTIFICATION = 'notification.send_mobile_notification'; /** * @var string */ public const EVENT_ACTION_SEND_NOTIFICATION = 'notification.send_notification'; /** * The maximum number of `include_player_ids` that can be sent within a single request. * * @var int */ protected const MAX_PLAYER_IDS_PER_REQUEST = 2000; public function __construct( private IntegrationHelper $integrationHelper, private NotificationModel $notificationModel, private AbstractNotificationApi $notificationApi, private EventDispatcherInterface $dispatcher, private DoNotContactModel $doNotContact, private TranslatorInterface $translator ) { } public static function getSubscribedEvents(): array { return [ CampaignEvents::CAMPAIGN_ON_BUILD => ['onCampaignBuild', 0], NotificationEvents::ON_CAMPAIGN_BATCH_ACTION => ['onCampaignBatchAction', 0], ]; } public function onCampaignBuild(CampaignBuilderEvent $event): void { $integration = $this->integrationHelper->getIntegrationObject('OneSignal'); if (!$integration || false === $integration->getIntegrationSettings()->getIsPublished()) { return; } $features = $integration->getSupportedFeatures(); if (in_array('mobile', $features)) { $event->addAction( static::EVENT_ACTION_SEND_MOBILE_NOTIFICATION, [ 'label' => 'mautic.notification.campaign.send_mobile_notification', 'description' => 'mautic.notification.campaign.send_mobile_notification.tooltip', 'batchEventName' => NotificationEvents::ON_CAMPAIGN_BATCH_ACTION, 'formType' => MobileNotificationSendType::class, 'formTypeOptions' => ['update_select' => 'campaignevent_properties_notification'], 'formTheme' => '@MauticNotification/FormTheme/NotificationSendList/_notificationsend_list_row.html.twig', 'timelineTemplate' => '@MauticNotification/SubscribedEvents/Timeline/index.html.twig', 'channel' => 'mobile_notification', 'channelIdField' => 'mobile_notification', ] ); } $event->addAction( static::EVENT_ACTION_SEND_NOTIFICATION, [ 'label' => 'mautic.notification.campaign.send_notification', 'description' => 'mautic.notification.campaign.send_notification.tooltip', 'batchEventName' => NotificationEvents::ON_CAMPAIGN_BATCH_ACTION, 'formType' => NotificationSendType::class, 'formTypeOptions' => ['update_select' => 'campaignevent_properties_notification'], 'formTheme' => '@MauticNotification/FormTheme/NotificationSendList/_notificationsend_list_row.html.twig', 'timelineTemplate' => '@MauticNotification/SubscribedEvents/Timeline/index.html.twig', 'channel' => 'notification', 'channelIdField' => 'notification', ] ); } public function onCampaignBatchAction(PendingEvent $event): void { if (!$event->checkContext(static::EVENT_ACTION_SEND_NOTIFICATION) && !$event->checkContext(static::EVENT_ACTION_SEND_MOBILE_NOTIFICATION)) { return; } $notificationId = $event->getEvent()->getProperties()['notification'] ?? null; $notification = $notificationId ? $this->notificationModel->getEntity((int) $notificationId) : null; if (!$notification) { $event->passAllWithError($this->translator->trans('mautic.notification.campaign.failed.missing_entity')); return; } if (!$notification->getIsPublished()) { $event->passAllWithError($this->translator->trans('mautic.notification.campaign.failed.unpublished')); return; } $event->setChannel('notification', $notification->getId()); if ($notification->getUrl()) { $this->sendNotificationPerLead($notification, $event); } else { $this->sendNotificationsInBatches($notification, $event); } } private function sendNotificationPerLead(Notification $notification, PendingEvent $event): void { foreach ($event->getPending() as $log) { if (!$this->isLeadContactable($event, $log)) { continue; } $playerIds = $this->getLeadPlayerIds($event, $log); if (!$playerIds) { continue; } $sendNotification = $this->buildNotificationToSend($notification, $log->getLead()); $response = $this->notificationApi->sendNotification($playerIds, $sendNotification); $this->processResponse($response, $event, $log, $notification, $sendNotification); } } private function sendNotificationsInBatches(Notification $notification, PendingEvent $event): void { $batches = $this->buildBatches($event, $notification); $processedLogs = []; foreach ($batches as $batch) { $sendNotification = $batch['sendNotification']; $playerIdsChunks = array_chunk($batch['playerIds'], static::MAX_PLAYER_IDS_PER_REQUEST, true); foreach ($playerIdsChunks as $playerIdsChunk) { $playerIds = array_keys($playerIdsChunk); $response = $this->notificationApi->sendNotification($playerIds, $sendNotification); foreach ($playerIdsChunk as $log) { if (!isset($processedLogs[$log->getId()])) { $processedLogs[$log->getId()] = $log; $this->processResponse($response, $event, $log, $notification, $sendNotification); } } } } } private function isLeadContactable(PendingEvent $event, LeadEventLog $log): bool { $contactable = DoNotContact::IS_CONTACTABLE === $this->doNotContact->isContactable($log->getLead(), 'notification'); if (!$contactable) { $event->passWithError($log, $this->translator->trans('mautic.notification.campaign.failed.not_contactable')); } return $contactable; } /** * @return string[] */ private function getLeadPlayerIds(PendingEvent $event, LeadEventLog $log): array { $playerIds = []; foreach ($log->getLead()->getPushIDs() as $pushID) { // Skip non-mobile PushIDs if this is a mobile event if ($event->checkContext(static::EVENT_ACTION_SEND_MOBILE_NOTIFICATION) && !$pushID->isMobile()) { continue; } // Skip mobile PushIDs if this is a non-mobile event if ($event->checkContext(static::EVENT_ACTION_SEND_NOTIFICATION) && $pushID->isMobile()) { continue; } $playerIds[] = $pushID->getPushID(); } if (!$playerIds) { $event->passWithError($log, $this->translator->trans('mautic.notification.campaign.failed.not_subscribed')); } return $playerIds; } private function buildNotificationToSend(Notification $notification, Lead $lead): Notification { /** @var TokenReplacementEvent $tokenEvent */ $tokenEvent = $this->dispatcher->dispatch( new TokenReplacementEvent( $notification->getMessage(), $lead, ['channel' => ['notification', $notification->getId()]] ), NotificationEvents::TOKEN_REPLACEMENT ); /** @var NotificationSendEvent $sendEvent */ $sendEvent = $this->dispatcher->dispatch( new NotificationSendEvent($tokenEvent->getContent(), $notification->getHeading(), $lead), NotificationEvents::NOTIFICATION_ON_SEND ); if ($url = $notification->getUrl()) { $url = $this->notificationApi->convertToTrackedUrl( $url, [ 'notification' => $notification->getId(), 'lead' => $lead->getId(), ], $notification ); } // prevent rewrite notification entity $sendNotification = clone $notification; $sendNotification->setUrl($url); $sendNotification->setMessage($sendEvent->getMessage()); $sendNotification->setHeading($sendEvent->getHeading()); return $sendNotification; } private function processResponse(ResponseInterface $response, PendingEvent $event, LeadEventLog $log, Notification $notification, Notification $sendNotification): void { // if for some reason the call failed, tell mautic to try again if (200 !== $response->getStatusCode()) { $event->fail($log, sprintf('%s (%s)', (string) $response->getBody(), $response->getStatusCode())); return; } $this->notificationModel->createStatEntry($notification, $log->getLead(), 'campaign.event', $event->getEvent()->getId()); $this->notificationModel->getRepository()->upCount($notification->getId()); $result = [ 'status' => 'mautic.notification.timeline.status.delivered', 'type' => 'mautic.notification.notification', 'id' => $notification->getId(), 'name' => $notification->getName(), 'heading' => $sendNotification->getHeading(), 'content' => $sendNotification->getMessage(), ]; $log->appendToMetadata($result); $event->pass($log); } /** * @return array<string,mixed[]> */ private function buildBatches(PendingEvent $event, Notification $notification): array { $batches = []; foreach ($event->getPending() as $log) { if (!$this->isLeadContactable($event, $log)) { continue; } $playerIds = $this->getLeadPlayerIds($event, $log); if (!$playerIds) { continue; } $sendNotification = $this->buildNotificationToSend($notification, $log->getLead()); $uniqueKey = md5(sprintf('[%s][%s]', $sendNotification->getHeading(), $sendNotification->getMessage())); if (!isset($batches[$uniqueKey])) { $batches[$uniqueKey] = [ 'sendNotification' => $sendNotification, 'playerIds' => [], ]; } foreach ($playerIds as $playerId) { $batches[$uniqueKey]['playerIds'][$playerId] = $log; } } return $batches; } }