From c0a07d93177b39f2a6081e04d3713b2d5ce5af36 Mon Sep 17 00:00:00 2001 From: Ferreira Date: Tue, 7 Mar 2017 14:43:31 -0300 Subject: [PATCH] Resolving some bugs to use this plugin in new version of mautic --- Api/SmsInfoBipApi.php | 8 +- Config/config.php | 13 +- EventListener/CampaignSubscriber.php | 110 +---- EventListener/ChannelSubscriber.php | 63 +++ EventListener/ConfigSubscriber.php | 42 ++ EventListener/FormSubscriber.php | 4 +- EventListener/MessageQueueSubscriber.php | 101 +++++ EventListener/SmsSubscriber.php | 158 +++++++ EventListener/StatsSubscriber.php | 31 ++ Helper/SmsHelper.php | 105 +++++ Model/SmsModel.php | 531 +++++++++++++++++++++++ 11 files changed, 1061 insertions(+), 105 deletions(-) create mode 100644 EventListener/ChannelSubscriber.php create mode 100644 EventListener/ConfigSubscriber.php create mode 100644 EventListener/MessageQueueSubscriber.php create mode 100644 EventListener/SmsSubscriber.php create mode 100644 EventListener/StatsSubscriber.php create mode 100644 Helper/SmsHelper.php create mode 100644 Model/SmsModel.php diff --git a/Api/SmsInfoBipApi.php b/Api/SmsInfoBipApi.php index 54f0062..fe18a86 100755 --- a/Api/SmsInfoBipApi.php +++ b/Api/SmsInfoBipApi.php @@ -12,19 +12,19 @@ use Mautic\CoreBundle\Factory\MauticFactory; use Mautic\CoreBundle\Helper\PhoneNumberHelper; use Mautic\PageBundle\Model\TrackableModel; +use MauticPlugin\InfoBipSmsBundle\Api\AbstractSmsApi; class SmsInfoBipApi extends AbstractSmsApi { private $username; private $password; - public function __construct(TrackableModel $pageTrackableModel = null, MauticFactory $factory, \Services_Twilio $client = null, PhoneNumberHelper $phoneNumberHelper = null, $sendingPhoneNumber, $username, $password) + public function __construct(TrackableModel $pageTrackableModel = null, MauticFactory $factory, PhoneNumberHelper $phoneNumberHelper = null, $sendingPhoneNumber, $username, $password) { - $this->client = $client; - parent::__construct($pageTrackableModel); - $this->username = $username; $this->password = $password; + + parent::__construct($pageTrackableModel); } public function sendSms($number, $content) diff --git a/Config/config.php b/Config/config.php index a21eea8..73926b0 100755 --- a/Config/config.php +++ b/Config/config.php @@ -16,11 +16,8 @@ 'class' => 'MauticPlugin\InfoBipSmsBundle\EventListener\CampaignSubscriber', 'arguments' => [ 'mautic.helper.core_parameters', - 'mautic.lead.model.lead', 'mautic.sms.model.sms', - 'mautic.sms.api', - 'mautic.helper.sms', - ], + ] ], 'mautic.sms.formbundle.subscriber' => [ 'class' => 'MauticPlugin\InfoBipSmsBundle\EventListener\FormSubscriber', @@ -77,7 +74,7 @@ ], 'helpers' => [ 'mautic.helper.sms' => [ - 'class' => 'Mautic\SmsBundle\Helper\SmsHelper', + 'class' => 'MauticPlugin\InfoBipSmsBundle\Helper\SmsHelper', 'arguments' => [ 'doctrine.orm.entity_manager', 'mautic.lead.model.lead', @@ -95,7 +92,6 @@ 'arguments' => [ 'mautic.page.model.trackable', 'mautic.factory', - 'mautic.twilio.service', 'mautic.helper.phone_number', '%mautic.sms_sending_phone_number%', '%mautic.sms_username%', @@ -106,9 +102,12 @@ ], 'models' => [ 'mautic.sms.model.sms' => [ - 'class' => 'Mautic\SmsBundle\Model\SmsModel', + 'class' => 'MauticPlugin\InfoBipSmsBundle\Model\SmsModel', 'arguments' => [ 'mautic.page.model.trackable', + 'mautic.lead.model.lead', + 'mautic.channel.model.queue', + 'mautic.sms.api' ], ], ], diff --git a/EventListener/CampaignSubscriber.php b/EventListener/CampaignSubscriber.php index 801191d..896a1ca 100755 --- a/EventListener/CampaignSubscriber.php +++ b/EventListener/CampaignSubscriber.php @@ -14,15 +14,9 @@ use Mautic\CampaignBundle\CampaignEvents; use Mautic\CampaignBundle\Event\CampaignBuilderEvent; use Mautic\CampaignBundle\Event\CampaignExecutionEvent; -use Mautic\CoreBundle\Event\TokenReplacementEvent; use Mautic\CoreBundle\EventListener\CommonSubscriber; use Mautic\CoreBundle\Helper\CoreParametersHelper; -use Mautic\LeadBundle\Entity\DoNotContact; -use Mautic\LeadBundle\Model\LeadModel; -use MauticPlugin\InfoBipSmsBundle\Api\AbstractSmsApi; -use Mautic\SmsBundle\Event\SmsSendEvent; -use Mautic\SmsBundle\Helper\SmsHelper; -use Mautic\SmsBundle\Model\SmsModel; +use MauticPlugin\InfoBipSmsBundle\Model\SmsModel; use Mautic\SmsBundle\SmsEvents; /** @@ -35,47 +29,23 @@ class CampaignSubscriber extends CommonSubscriber */ protected $coreParametersHelper; - /** - * @var LeadModel - */ - protected $leadModel; - /** * @var SmsModel */ protected $smsModel; - /** - * @var AbstractSmsApi - */ - protected $smsApi; - - /** - * @var smsHelper - */ - protected $smsHelper; - /** * CampaignSubscriber constructor. * * @param CoreParametersHelper $coreParametersHelper - * @param LeadModel $leadModel * @param SmsModel $smsModel - * @param AbstractSmsApi $smsApi - * @param SmsHelper $smsHelper */ public function __construct( CoreParametersHelper $coreParametersHelper, - LeadModel $leadModel, - SmsModel $smsModel, - AbstractSmsApi $smsApi, - SmsHelper $smsHelper + SmsModel $smsModel ) { $this->coreParametersHelper = $coreParametersHelper; - $this->leadModel = $leadModel; $this->smsModel = $smsModel; - $this->smsApi = $smsApi; - $this->smsHelper = $smsHelper; } /** @@ -98,13 +68,15 @@ public function onCampaignBuild(CampaignBuilderEvent $event) $event->addAction( 'sms.send_text_sms', [ - 'label' => 'mautic.campaign.infobip_sms.send_text_sms', + 'label' => 'mautic.campaign.sms.send_text_sms', 'description' => 'mautic.campaign.sms.send_text_sms.tooltip', 'eventName' => SmsEvents::ON_CAMPAIGN_TRIGGER_ACTION, 'formType' => 'smssend_list', 'formTypeOptions' => ['update_select' => 'campaignevent_properties_sms'], 'formTheme' => 'MauticSmsBundle:FormTheme\SmsSendList', 'timelineTemplate' => 'MauticSmsBundle:SubscribedEvents\Timeline:index.html.php', + 'channel' => 'sms', + 'channelIdField' => 'sms', ] ); } @@ -115,74 +87,28 @@ public function onCampaignBuild(CampaignBuilderEvent $event) */ public function onCampaignTriggerAction(CampaignExecutionEvent $event) { - $lead = $event->getLead(); - - if ($this->leadModel->isContactable($lead, 'sms') !== DoNotContact::IS_CONTACTABLE) { - return $event->setFailed('mautic.sms.campaign.failed.not_contactable'); - } - - $leadPhoneNumber = $lead->getFieldValue('mobile'); - - if (empty($leadPhoneNumber)) { - $leadPhoneNumber = $lead->getFieldValue('phone'); - } - - if (empty($leadPhoneNumber)) { - return $event->setFailed('mautic.sms.campaign.failed.missing_number'); - } - + $lead = $event->getLead(); $smsId = (int) $event->getConfig()['sms']; $sms = $this->smsModel->getEntity($smsId); - if ($sms->getId() !== $smsId) { + if (!$sms) { return $event->setFailed('mautic.sms.campaign.failed.missing_entity'); } - $smsEvent = new SmsSendEvent($sms->getMessage(), $lead); - $smsEvent->setSmsId($smsId); - $this->dispatcher->dispatch(SmsEvents::SMS_ON_SEND, $smsEvent); - - $tokenEvent = $this->dispatcher->dispatch( - SmsEvents::TOKEN_REPLACEMENT, - new TokenReplacementEvent( - $smsEvent->getContent(), - $lead, - ['channel' => ['sms', $sms->getId()]] - ) - ); - - $metadata = $this->smsApi->sendSms($leadPhoneNumber, $tokenEvent->getContent()); - - $defaultFrequencyNumber = $this->coreParametersHelper->getParameter('sms_frequency_number'); - $defaultFrequencyTime = $this->coreParametersHelper->getParameter('sms_frequency_time'); + $result = $this->smsModel->sendSms($sms, $lead, ['channel' => ['campaign.event', $event->getEvent()['id']]])[$lead->getId()]; - /** @var \Mautic\LeadBundle\Entity\FrequencyRuleRepository $frequencyRulesRepo */ - $frequencyRulesRepo = $this->leadModel->getFrequencyRuleRepository(); - - $leadIds = $lead->getId(); - - $dontSendTo = $frequencyRulesRepo->getAppliedFrequencyRules('sms', $leadIds, $defaultFrequencyNumber, $defaultFrequencyTime); - - if (!empty($dontSendTo) and $dontSendTo[0]['lead_id'] != $lead->getId()) { - $metadata = $this->smsApi->sendSms($leadPhoneNumber, $smsEvent->getContent()); - } - - // If there was a problem sending at this point, it's an API problem and should be requeued - if ($metadata === false) { + if ('Authenticate' === $result['status']) { + // Don't fail the event but reschedule it for later return $event->setResult(false); } - $this->smsModel->createStatEntry($sms, $lead); - $this->smsModel->getRepository()->upCount($smsId); - $event->setChannel('sms', $sms->getId()); - $event->setResult( - [ - 'type' => 'mautic.sms.sms', - 'status' => 'mautic.sms.timeline.status.delivered', - 'id' => $sms->getId(), - 'name' => $sms->getName(), - 'content' => $tokenEvent->getContent(), - ] - ); + if (!empty($result['sent'])) { + $event->setChannel('sms', $sms->getId()); + $event->setResult($result); + } else { + $result['failed'] = true; + $result['reason'] = $result['status']; + $event->setResult($result); + } } } diff --git a/EventListener/ChannelSubscriber.php b/EventListener/ChannelSubscriber.php new file mode 100644 index 0000000..3a5e4f1 --- /dev/null +++ b/EventListener/ChannelSubscriber.php @@ -0,0 +1,63 @@ + ['onAddChannel', 90], + ]; + } + + /** + * @param ChannelEvent $event + */ + public function onAddChannel(ChannelEvent $event) + { + if (!empty($this->params['sms_enabled'])) { + $event->addChannel( + 'sms', + [ + MessageModel::CHANNEL_FEATURE => [ + 'campaignAction' => 'sms.send_text_sms', + 'campaignDecisionsSupported' => [ + 'page.pagehit', + 'asset.download', + 'form.submit', + ], + 'lookupFormType' => 'sms_list', + 'repository' => 'MauticSmsBundle:Sms', + ], + LeadModel::CHANNEL_FEATURE => [], + ReportModel::CHANNEL_FEATURE => [ + 'table' => 'sms_messages', + ], + ] + ); + } + } +} diff --git a/EventListener/ConfigSubscriber.php b/EventListener/ConfigSubscriber.php new file mode 100644 index 0000000..5a403c3 --- /dev/null +++ b/EventListener/ConfigSubscriber.php @@ -0,0 +1,42 @@ + ['onConfigGenerate', 0], + ]; + } + + public function onConfigGenerate(ConfigBuilderEvent $event) + { + $event->addForm([ + 'bundle' => 'SmsBundle', + 'formAlias' => 'smsconfig', + 'formTheme' => 'MauticSmsBundle:FormTheme\Config', + 'parameters' => $event->getParametersFromConfig('MauticSmsBundle'), + ]); + } +} diff --git a/EventListener/FormSubscriber.php b/EventListener/FormSubscriber.php index 36e8fc5..777f0b3 100644 --- a/EventListener/FormSubscriber.php +++ b/EventListener/FormSubscriber.php @@ -16,8 +16,8 @@ use Mautic\FormBundle\FormEvents; use MauticPlugin\InfoBipSmsBundle\Api\AbstractSmsApi; use Mautic\SmsBundle\Event\SmsSendEvent; -use Mautic\SmsBundle\Helper\SmsHelper; -use Mautic\SmsBundle\Model\SmsModel; +use MauticPlugin\InfoBipSmsBundle\Helper\SmsHelper; +use MauticPlugin\InfoBipSmsBundle\Model\SmsModel; use Mautic\SmsBundle\SmsEvents; use Mautic\CoreBundle\Event\TokenReplacementEvent; use Mautic\LeadBundle\Model\LeadModel; diff --git a/EventListener/MessageQueueSubscriber.php b/EventListener/MessageQueueSubscriber.php new file mode 100644 index 0000000..2c08423 --- /dev/null +++ b/EventListener/MessageQueueSubscriber.php @@ -0,0 +1,101 @@ +model = $model; + } + + /** + * @return array + */ + public static function getSubscribedEvents() + { + return [ + ChannelEvents::PROCESS_MESSAGE_QUEUE_BATCH => ['onProcessMessageQueueBatch', 0], + ]; + } + + /** + * Sends campaign emails. + * + * @param MessageQueueBatchProcessEvent $event + */ + public function onProcessMessageQueueBatch(MessageQueueBatchProcessEvent $event) + { + if (!$event->checkContext('sms')) { + return; + } + + $messages = $event->getMessages(); + $id = $event->getChannelId(); + $sms = $this->model->getEntity($id); + $sendTo = []; + $messagesByContact = []; + + /** @var MessageQueue $message */ + foreach ($messages as $id => $message) { + if ($sms && $message->getLead()) { + $contact = $message->getLead(); + $mobile = $contact->getMobile(); + $phone = $contact->getPhone(); + if (empty($mobile) && empty($phone)) { + $message->setProcessed(); + $message->setSuccess(); + } + $sendTo[$contact->getId()] = $contact; + $messagesByContact[$contact->getId()] = $message; + } else { + $message->setFailed(); + } + } + + if (count($sendTo)) { + $options['resend_message_queue'] = $messagesByContact; + $results = $this->model->sendSms($sms, $sendTo, $options); + + foreach ($messagesByContact as $contactId => $message) { + if (!$message->isProcessed()) { + $message->setProcessed(); + $message->setMetadata($results[$contactId]); + if ($results[$contactId]['sent']) { + $message->setSuccess(); + } + } + } + } + + $event->stopPropagation(); + } +} diff --git a/EventListener/SmsSubscriber.php b/EventListener/SmsSubscriber.php new file mode 100644 index 0000000..65f1c38 --- /dev/null +++ b/EventListener/SmsSubscriber.php @@ -0,0 +1,158 @@ +auditLogModel = $auditLogModel; + $this->trackableModel = $trackableModel; + $this->pageTokenHelper = $pageTokenHelper; + $this->assetTokenHelper = $assetTokenHelper; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + SmsEvents::SMS_POST_SAVE => ['onPostSave', 0], + SmsEvents::SMS_POST_DELETE => ['onDelete', 0], + SmsEvents::TOKEN_REPLACEMENT => ['onTokenReplacement', 0], + ]; + } + + /** + * Add an entry to the audit log. + * + * @param SmsEvent $event + */ + public function onPostSave(SmsEvent $event) + { + $entity = $event->getSms(); + if ($details = $event->getChanges()) { + $log = [ + 'bundle' => 'sms', + 'object' => 'sms', + 'objectId' => $entity->getId(), + 'action' => ($event->isNew()) ? 'create' : 'update', + 'details' => $details, + ]; + $this->auditLogModel->writeToLog($log); + } + } + + /** + * Add a delete entry to the audit log. + * + * @param SmsEvent $event + */ + public function onDelete(SmsEvent $event) + { + $entity = $event->getSms(); + $log = [ + 'bundle' => 'sms', + 'object' => 'sms', + 'objectId' => $entity->deletedId, + 'action' => 'delete', + 'details' => ['name' => $entity->getName()], + ]; + $this->auditLogModel->writeToLog($log); + } + + /** + * @param TokenReplacementEvent $event + */ + public function onTokenReplacement(TokenReplacementEvent $event) + { + /** @var Lead $lead */ + $lead = $event->getLead(); + $content = $event->getContent(); + $clickthrough = $event->getClickthrough(); + + if ($content) { + $tokens = array_merge( + TokenHelper::findLeadTokens($content, $lead->getProfileFields()), + $this->pageTokenHelper->findPageTokens($content, $clickthrough), + $this->assetTokenHelper->findAssetTokens($content, $clickthrough) + ); + + list($content, $trackables) = $this->trackableModel->parseContentForTrackables( + $content, + $tokens, + 'sms', + $clickthrough['channel'][1] + ); + + /** + * @var string + * @var Trackable $trackable + */ + foreach ($trackables as $token => $trackable) { + $tokens[$token] = $this->trackableModel->generateTrackableUrl($trackable, $clickthrough, true); + } + + $content = str_replace(array_keys($tokens), array_values($tokens), $content); + + $event->setContent($content); + } + } +} diff --git a/EventListener/StatsSubscriber.php b/EventListener/StatsSubscriber.php new file mode 100644 index 0000000..fd81426 --- /dev/null +++ b/EventListener/StatsSubscriber.php @@ -0,0 +1,31 @@ +addContactRestrictedRepositories($em, 'MauticSmsBundle:Stat'); + } +} diff --git a/Helper/SmsHelper.php b/Helper/SmsHelper.php new file mode 100644 index 0000000..b6a5db2 --- /dev/null +++ b/Helper/SmsHelper.php @@ -0,0 +1,105 @@ +em = $em; + $this->leadModel = $leadModel; + $this->phoneNumberHelper = $phoneNumberHelper; + $this->smsModel = $smsModel; + $this->smsFrequencyNumber = $smsFrequencyNumber; + } + + public function unsubscribe($number) + { + $number = $this->phoneNumberHelper->format($number, PhoneNumberFormat::E164); + + /** @var \Mautic\LeadBundle\Entity\LeadRepository $repo */ + $repo = $this->em->getRepository('MauticLeadBundle:Lead'); + + $args = [ + 'filter' => [ + 'force' => [ + [ + 'column' => 'mobile', + 'expr' => 'eq', + 'value' => $number, + ], + ], + ], + ]; + + $leads = $repo->getEntities($args); + + if (!empty($leads)) { + $lead = array_shift($leads); + } else { + // Try to find the lead based on the given phone number + $args['filter']['force'][0]['column'] = 'phone'; + + $leads = $repo->getEntities($args); + + if (!empty($leads)) { + $lead = array_shift($leads); + } else { + return false; + } + } + + return $this->leadModel->addDncForLead($lead, 'sms', null, DoNotContact::UNSUBSCRIBED); + } +} diff --git a/Model/SmsModel.php b/Model/SmsModel.php new file mode 100644 index 0000000..8cd7c5f --- /dev/null +++ b/Model/SmsModel.php @@ -0,0 +1,531 @@ +pageTrackableModel = $pageTrackableModel; + $this->leadModel = $leadModel; + $this->messageQueueModel = $messageQueueModel; + $this->smsApi = $smsApi; + } + + /** + * {@inheritdoc} + * + * @return \Mautic\SmsBundle\Entity\SmsRepository + */ + public function getRepository() + { + return $this->em->getRepository('MauticSmsBundle:Sms'); + } + + /** + * @return \Mautic\SmsBundle\Entity\StatRepository + */ + public function getStatRepository() + { + return $this->em->getRepository('MauticSmsBundle:Stat'); + } + + /** + * {@inheritdoc} + */ + public function getPermissionBase() + { + return 'sms:smses'; + } + + /** + * Save an array of entities. + * + * @param $entities + * @param $unlock + * + * @return array + */ + public function saveEntities($entities, $unlock = true) + { + //iterate over the results so the events are dispatched on each delete + $batchSize = 20; + foreach ($entities as $k => $entity) { + $isNew = ($entity->getId()) ? false : true; + + //set some defaults + $this->setTimestamps($entity, $isNew, $unlock); + + if ($dispatchEvent = $entity instanceof Sms) { + $event = $this->dispatchEvent('pre_save', $entity, $isNew); + } + + $this->getRepository()->saveEntity($entity, false); + + if ($dispatchEvent) { + $this->dispatchEvent('post_save', $entity, $isNew, $event); + } + + if ((($k + 1) % $batchSize) === 0) { + $this->em->flush(); + } + } + $this->em->flush(); + } + + /** + * {@inheritdoc} + * + * @param $entity + * @param $formFactory + * @param null $action + * @param array $options + * + * @return mixed + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * @throws MethodNotAllowedHttpException + */ + public function createForm($entity, $formFactory, $action = null, $options = []) + { + if (!$entity instanceof Sms) { + throw new MethodNotAllowedHttpException(['Sms']); + } + if (!empty($action)) { + $options['action'] = $action; + } + + return $formFactory->create('sms', $entity, $options); + } + + /** + * Get a specific entity or generate a new one if id is empty. + * + * @param $id + * + * @return null|Sms + */ + public function getEntity($id = null) + { + if ($id === null) { + $entity = new Sms(); + } else { + $entity = parent::getEntity($id); + } + + return $entity; + } + + /** + * @param Sms $sms + * @param $sendTo + * @param array $options + * + * @return array + */ + public function sendSms(Sms $sms, $sendTo, $options = []) + { + $channel = (isset($options['channel'])) ? $options['channel'] : null; + + if ($sendTo instanceof Lead) { + $sendTo = [$sendTo]; + } elseif (!is_array($sendTo)) { + $sendTo = [$sendTo]; + } + + $sentCount = 0; + $results = []; + $contacts = []; + $fetchContacts = []; + foreach ($sendTo as $lead) { + if (!$lead instanceof Lead) { + $fetchContacts[] = $lead; + } else { + $contacts[$lead->getId()] = $lead; + } + } + + if ($fetchContacts) { + $foundContacts = $this->leadModel->getEntities( + [ + 'ids' => $fetchContacts, + ] + ); + + foreach ($foundContacts as $contact) { + $contacts[$contact->getId()] = $contact; + } + } + $contactIds = array_keys($contacts); + + /** @var DoNotContactRepository $dncRepo */ + $dncRepo = $this->em->getRepository('MauticLeadBundle:DoNotContact'); + $dnc = $dncRepo->getChannelList('sms', $contactIds); + + if (!empty($dnc)) { + foreach ($dnc as $removeMeId => $removeMeReason) { + $results[$removeMeId] = [ + 'sent' => false, + 'status' => 'mautic.sms.campaign.failed.not_contactable', + ]; + + unset($contacts[$removeMeId], $contactIds[$removeMeId]); + } + } + + if (!empty($contacts)) { + $messageQueue = (isset($options['resend_message_queue'])) ? $options['resend_message_queue'] : null; + $campaignEventId = (is_array($channel) && 'campaign.event' === $channel[0] && !empty($channel[1])) ? $channel[1] : null; + + $queued = $this->messageQueueModel->processFrequencyRules( + $contacts, + 'sms', + $sms->getId(), + $campaignEventId, + 3, + MessageQueue::PRIORITY_NORMAL, + $messageQueue, + 'sms_message_stats' + ); + + if ($queued) { + foreach ($queued as $queue) { + $results[$queue] = [ + 'sent' => false, + 'status' => 'mautic.sms.timeline.status.scheduled', + ]; + + unset($contacts[$queue]); + } + } + + $stats = []; + if (count($contacts)) { + /** @var Lead $lead */ + foreach ($contacts as $lead) { + $leadId = $lead->getId(); + + $leadPhoneNumber = $lead->getMobile(); + if (empty($leadPhoneNumber)) { + $leadPhoneNumber = $lead->getPhone(); + } + + if (empty($leadPhoneNumber)) { + $results[$leadId] = [ + 'sent' => false, + 'status' => 'mautic.sms.campaign.failed.missing_number', + ]; + } + + $smsEvent = new SmsSendEvent($sms->getMessage(), $lead); + $smsEvent->setSmsId($sms->getId()); + $this->dispatcher->dispatch(SmsEvents::SMS_ON_SEND, $smsEvent); + + $tokenEvent = $this->dispatcher->dispatch( + SmsEvents::TOKEN_REPLACEMENT, + new TokenReplacementEvent( + $smsEvent->getContent(), + $lead, + ['channel' => ['sms', $sms->getId()]] + ) + ); + + $sendResult = [ + 'sent' => false, + 'type' => 'mautic.sms.sms', + 'status' => 'mautic.sms.timeline.status.delivered', + 'id' => $sms->getId(), + 'name' => $sms->getName(), + 'content' => $tokenEvent->getContent(), + ]; + + $metadata = $this->smsApi->sendSms($leadPhoneNumber, $tokenEvent->getContent()); + + if (true !== $metadata) { + $sendResult['status'] = $metadata; + } else { + $sendResult['sent'] = true; + $stats[] = $this->createStatEntry($sms, $lead, $channel, false); + ++$sentCount; + } + + $results[$leadId] = $sendResult; + + unset($smsEvent, $tokenEvent, $sendResult, $metadata); + } + } + } + + if ($sentCount) { + $this->getRepository()->upCount($sms->getId(), 'sent', $sentCount); + $this->getStatRepository()->saveEntities($stats); + $this->em->clear(Stat::class); + } + + return $results; + } + + /** + * @param Sms $sms + * @param Lead $lead + * @param null $source + * @param bool $persist + * + * @return Stat + */ + public function createStatEntry(Sms $sms, Lead $lead, $source = null, $persist = true) + { + $stat = new Stat(); + $stat->setDateSent(new \DateTime()); + $stat->setLead($lead); + $stat->setSms($sms); + if (is_array($source)) { + $stat->setSourceId($source[1]); + $source = $source[0]; + } + $stat->setSource($source); + + if ($persist) { + $this->getStatRepository()->saveEntity($stat); + } + + return $stat; + } + + /** + * {@inheritdoc} + * + * @param $action + * @param $event + * @param $entity + * @param $isNew + * + * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException + */ + protected function dispatchEvent($action, &$entity, $isNew = false, Event $event = null) + { + if (!$entity instanceof Sms) { + throw new MethodNotAllowedHttpException(['Sms']); + } + + switch ($action) { + case 'pre_save': + $name = SmsEvents::SMS_PRE_SAVE; + break; + case 'post_save': + $name = SmsEvents::SMS_POST_SAVE; + break; + case 'pre_delete': + $name = SmsEvents::SMS_PRE_DELETE; + break; + case 'post_delete': + $name = SmsEvents::SMS_POST_DELETE; + break; + default: + return; + } + + if ($this->dispatcher->hasListeners($name)) { + if (empty($event)) { + $event = new SmsEvent($entity, $isNew); + $event->setEntityManager($this->em); + } + + $this->dispatcher->dispatch($name, $event); + + return $event; + } else { + return; + } + } + + /** + * Joins the page table and limits created_by to currently logged in user. + * + * @param QueryBuilder $q + */ + public function limitQueryToCreator(QueryBuilder &$q) + { + $q->join('t', MAUTIC_TABLE_PREFIX.'sms_messages', 's', 's.id = t.sms_id') + ->andWhere('s.created_by = :userId') + ->setParameter('userId', $this->userHelper->getUser()->getId()); + } + + /** + * Get line chart data of hits. + * + * @param char $unit {@link php.net/manual/en/function.date.php#refsect1-function.date-parameters} + * @param \DateTime $dateFrom + * @param \DateTime $dateTo + * @param string $dateFormat + * @param array $filter + * @param bool $canViewOthers + * + * @return array + */ + public function getHitsLineChartData($unit, \DateTime $dateFrom, \DateTime $dateTo, $dateFormat = null, $filter = [], $canViewOthers = true) + { + $flag = null; + + if (isset($filter['flag'])) { + $flag = $filter['flag']; + unset($filter['flag']); + } + + $chart = new LineChart($unit, $dateFrom, $dateTo, $dateFormat); + $query = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo); + + if (!$flag || $flag === 'total_and_unique') { + $q = $query->prepareTimeDataQuery('sms_message_stats', 'date_sent', $filter); + + if (!$canViewOthers) { + $this->limitQueryToCreator($q); + } + + $data = $query->loadAndBuildTimeData($q); + $chart->setDataset($this->translator->trans('mautic.sms.show.total.sent'), $data); + } + + return $chart->render(); + } + + /** + * @param $idHash + * + * @return Stat + */ + public function getSmsStatus($idHash) + { + return $this->getStatRepository()->getSmsStatus($idHash); + } + + /** + * Search for an sms stat by sms and lead IDs. + * + * @param $smsId + * @param $leadId + * + * @return array + */ + public function getSmsStatByLeadId($smsId, $leadId) + { + return $this->getStatRepository()->findBy( + [ + 'sms' => (int) $smsId, + 'lead' => (int) $leadId, + ], + ['dateSent' => 'DESC'] + ); + } + + /** + * Get an array of tracked links. + * + * @param $smsId + * + * @return array + */ + public function getSmsClickStats($smsId) + { + return $this->pageTrackableModel->getTrackableList('sms', $smsId); + } + + /** + * @param $type + * @param string $filter + * @param int $limit + * @param int $start + * @param array $options + * + * @return array + */ + public function getLookupResults($type, $filter = '', $limit = 10, $start = 0, $options = []) + { + $results = []; + switch ($type) { + case 'sms': + $entities = $this->getRepository()->getSmsList( + $filter, + $limit, + $start, + $this->security->isGranted($this->getPermissionBase().':viewother'), + isset($options['template']) ? $options['template'] : false + ); + + foreach ($entities as $entity) { + $results[$entity['language']][$entity['id']] = $entity['name']; + } + + //sort by language + ksort($results); + + break; + } + + return $results; + } +}