/** * Add/remove leads from campaigns based on lead list changes * * @param ListChangeEvent $event */ public function onLeadListChange(ListChangeEvent $event) { $lead = $event->getLead(); $list = $event->getList(); $action = $event->wasAdded() ? 'added' : 'removed'; $repo = $this->campaignModel->getRepository(); //get campaigns for the list $listCampaigns = $repo->getPublishedCampaignsByLeadLists($list->getId()); $leadLists = $this->leadModel->getLists($lead, true); $leadListIds = array_keys($leadLists); // If the lead was removed then don't count it if ($action == 'removed') { $key = array_search($list->getId(), $leadListIds); unset($leadListIds[$key]); } if (!empty($listCampaigns)) { foreach ($listCampaigns as $c) { $campaign = $this->em->getReference('MauticCampaignBundle:Campaign', $c['id']); if (!isset($campaignLists[$c['id']])) { $campaignLists[$c['id']] = array_keys($c['lists']); } if ($action == 'added') { $this->campaignModel->addLead($campaign, $lead); } else { if (array_intersect($leadListIds, $campaignLists[$c['id']])) { continue; } $this->campaignModel->removeLead($campaign, $lead); } unset($campaign); } } }
/** * @param CampaignExecutionEvent $event */ public function onCampaignTriggerActionChangeStage(CampaignExecutionEvent $event) { $stageChange = false; $lead = $event->getLead(); $leadStage = null; if ($lead instanceof Lead) { $leadStage = $lead->getStage(); } $stageId = (int) $event->getConfig()['stage']; $stageToChangeTo = $this->stageModel->getEntity($stageId); if ($stageToChangeTo != null && $stageToChangeTo->isPublished()) { if ($leadStage && $leadStage->getWeight() <= $stageToChangeTo->getWeight()) { $stageChange = true; } elseif (!$leadStage) { $stageChange = true; } } if ($stageChange) { $parsed = explode('.', $stageToChangeTo->getName()); $lead->stageChangeLogEntry($parsed[0], $stageToChangeTo->getId() . ": " . $stageToChangeTo->getName(), $event->getName()); $lead->setStage($stageToChangeTo); $this->leadModel->saveEntity($lead); } return $event->setResult($stageChange); }
/** * @param Event|int $event * @param Campaign $campaign * @param \Mautic\LeadBundle\Entity\Lead|null $lead * @param \Mautic\CoreBundle\Entity\IpAddress|null $ipAddress * @param bool $systemTriggered * * @return LeadEventLog * * @throws \Doctrine\ORM\ORMException */ public function getLogEntity($event, $campaign, $lead = null, $ipAddress = null, $systemTriggered = false) { $log = new LeadEventLog(); if ($ipAddress == null) { // Lead triggered from system IP $ipAddress = $this->ipLookupHelper->getIpAddress(); } $log->setIpAddress($ipAddress); if (!$event instanceof Event) { $event = $this->em->getReference('MauticCampaignBundle:Event', $event); } $log->setEvent($event); if (!$campaign instanceof Campaign) { $campaign = $this->em->getReference('MauticCampaignBundle:Campaign', $campaign); } $log->setCampaign($campaign); if ($lead == null) { $lead = $this->leadModel->getCurrentLead(); } $log->setLead($lead); $log->setDateTriggered(new \DateTime()); $log->setSystemTriggered($systemTriggered); // Save some RAM for batch processing unset($event, $campaign, $lead); return $log; }
/** * @param CampaignExecutionEvent $event */ public function onCampaignTriggerCondition(CampaignExecutionEvent $event) { $lead = $event->getLead(); if (!$lead || !$lead->getId()) { return $event->setResult(false); } $operators = $this->leadModel->getFilterExpressionFunctions(); $result = $this->leadFieldModel->getRepository()->compareValue($lead->getId(), $event->getConfig()['field'], $event->getConfig()['value'], $operators[$event->getConfig()['operator']]['expr']); return $event->setResult($result); }
/** * Triggers a specific stage change (this function is not being used any more but leaving it in case we do want to move stages through different actions) * * @param $type * @param mixed $eventDetails passthrough from function triggering action to the callback function * @param mixed $typeId Something unique to the triggering event to prevent unnecessary duplicate calls * @param Lead $lead * * @return void */ public function triggerAction($type, $eventDetails = null, $typeId = null, Lead $lead = null) { //only trigger actions for anonymous users if (!$this->security->isAnonymous()) { return; } if ($typeId !== null && MAUTIC_ENV === 'prod') { $triggeredEvents = $this->session->get('mautic.triggered.stage.actions', array()); if (in_array($typeId, $triggeredEvents)) { return; } $triggeredEvents[] = $typeId; $this->session->set('mautic.triggered.stage.actions', $triggeredEvents); } //find all the actions for published stages /** @var \Mautic\StageBundle\Entity\StageRepository $repo */ $repo = $this->getRepository(); $availableStages = $repo->getPublishedByType($type); if (null === $lead) { $lead = $this->leadModel->getCurrentLead(); if (null === $lead || !$lead->getId()) { return; } } //get available actions $availableActions = $this->getStageActions(); //get a list of actions that has already been performed on this lead $completedActions = $repo->getCompletedLeadActions($type, $lead->getId()); $persist = array(); foreach ($availableStages as $action) { //if it's already been done, then skip it if (isset($completedActions[$action->getId()])) { continue; } //make sure the action still exists if (!isset($availableActions['actions'][$action->getName()])) { continue; } $parsed = explode('.', $action->getName()); $lead->stageChangeLogEntry($parsed[0], $action->getId() . ": " . $action->getName(), 'Moved by an action'); $lead->setStage($action); $log = new LeadStageLog(); $log->setStage($action); $log->setLead($lead); $log->setDateFired(new \DateTime()); $persist[] = $log; } if (!empty($persist)) { $this->leadModel->saveEntity($lead); $this->getRepository()->saveEntities($persist); // Detach logs to reserve memory $this->em->clear('Mautic\\StageBundle\\Entity\\LeadStageLog'); } }
/** * @param PageDisplayEvent $event */ public function onPageDisplay(PageDisplayEvent $event) { $page = $event->getPage(); $leadId = $this->security->isAnonymous() ? $this->leadModel->getCurrentLead()->getId() : null; $tokens = $this->generateTokensFromContent($event, $leadId, ['page', $page->getId()]); $content = $event->getContent(); if (!empty($tokens)) { $content = str_ireplace(array_keys($tokens), $tokens, $content); } $event->setContent($content); }
/** * @param CampaignExecutionEvent $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'); } $smsId = (int) $event->getConfig()['sms']; $sms = $this->smsModel->getEntity($smsId); if ($sms->getId() !== $smsId) { 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->factory->getParameter('sms_frequency_number'); $defaultFrequencyTime = $this->factory->getParameter('sms_frequency_time'); /** @var \Mautic\LeadBundle\Entity\FrequencyRuleRepository $frequencyRulesRepo */ $frequencyRulesRepo = $this->leadModel->getFrequencyRuleRepository(); $leadIds = $lead->getId(); $dontSendTo = $frequencyRulesRepo->getAppliedFrequencyRules('sms', $leadIds, null, $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) { 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()]); }
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); }
/** * @param CampaignExecutionEvent $event */ public function onCampaignTriggerAction(CampaignExecutionEvent $event) { $lead = $event->getLead(); if ($this->leadModel->isContactable($lead, 'notification') !== DoNotContact::IS_CONTACTABLE) { return $event->setFailed('mautic.notification.campaign.failed.not_contactable'); } // If lead has subscribed on multiple devices, get all of them. /** @var \Mautic\NotificationBundle\Entity\PushID[] $pushIDs */ $pushIDs = $lead->getPushIDs(); $playerID = []; foreach ($pushIDs as $pushID) { $playerID[] = $pushID->getPushID(); } if (empty($playerID)) { return $event->setFailed('mautic.notification.campaign.failed.not_subscribed'); } $notificationId = (int) $event->getConfig()['notification']; /** @var \Mautic\NotificationBundle\Entity\Notification $notification */ $notification = $this->notificationModel->getEntity($notificationId); if ($notification->getId() !== $notificationId) { return $event->setFailed('mautic.notification.campaign.failed.missing_entity'); } if ($url = $notification->getUrl()) { $url = $this->notificationApi->convertToTrackedUrl($url, ['notification' => $notification->getId(), 'lead' => $lead->getId()]); } $tokenEvent = $this->dispatcher->dispatch(NotificationEvents::TOKEN_REPLACEMENT, new TokenReplacementEvent($notification->getMessage(), $lead, ['channel' => ['notification', $notification->getId()]])); $sendEvent = $this->dispatcher->dispatch(NotificationEvents::NOTIFICATION_ON_SEND, new NotificationSendEvent($tokenEvent->getContent(), $notification->getHeading(), $lead)); $response = $this->notificationApi->sendNotification($playerID, $sendEvent->getMessage(), $sendEvent->getHeading(), $url); $event->setChannel('notification', $notification->getId()); // If for some reason the call failed, tell mautic to try again by return false if ($response->code !== 200) { return $event->setResult(false); } $this->notificationModel->createStatEntry($notification, $lead); $this->notificationModel->getRepository()->upCount($notificationId); $result = ['status' => 'mautic.notification.timeline.status.delivered', 'type' => 'mautic.notification.notification', 'id' => $notification->getId(), 'name' => $notification->getName(), 'heading' => $event->getHeading(), 'content' => $event->getMessage()]; $event->setResult($result); }
/** * Gets the campaigns a specific lead is part of * * @param Lead $lead * @param bool $forList * * @return mixed */ public function getLeadCampaigns(Lead $lead = null, $forList = false) { static $campaigns = array(); if ($lead === null) { $lead = $this->leadModel->getCurrentLead(); } if (!isset($campaigns[$lead->getId()])) { $repo = $this->getRepository(); $leadId = $lead->getId(); //get the campaigns the lead is currently part of $campaigns[$leadId] = $repo->getPublishedCampaigns(null, $lead->getId(), $forList); } return $campaigns[$lead->getId()]; }
/** * @param $email * @param int $reason * @param string $comments * @param bool $flush * @param null $leadId * * @return array */ public function setEmailDoNotContact($email, $reason = DoNotContact::BOUNCED, $comments = '', $flush = true, $leadId = null) { /** @var \Mautic\LeadBundle\Entity\LeadRepository $leadRepo */ $leadRepo = $this->em->getRepository('MauticLeadBundle:Lead'); if (null === $leadId) { $leadId = (array) $leadRepo->getLeadByEmail($email, true); } elseif (!is_array($leadId)) { $leadId = [$leadId]; } $dnc = []; foreach ($leadId as $lead) { $dnc[] = $this->leadModel->addDncForLead($this->em->getReference('MauticLeadBundle:Lead', $lead), 'email', $comments, $reason, $flush); } return $dnc; }
/** * Triggers a specific event * * @param array $event * @param Lead $lead * @param bool $force * * @return bool Was event triggered */ public function triggerEvent($event, Lead $lead = null, $force = false) { //only trigger events for anonymous users if (!$force && !$this->security->isAnonymous()) { return false; } if ($lead == null) { $lead = $this->leadModel->getCurrentLead(); } if (!$force) { //get a list of events that has already been performed on this lead $appliedEvents = $this->getEventRepository()->getLeadTriggeredEvents($lead->getId()); //if it's already been done, then skip it if (isset($appliedEvents[$event['id']])) { return false; } } $availableEvents = $this->getEvents(); $eventType = $event['type']; //make sure the event still exists if (!isset($availableEvents[$eventType])) { return false; } $settings = $availableEvents[$eventType]; $args = array('event' => $event, 'lead' => $lead, 'factory' => $this->factory, 'config' => $event['properties']); if (is_callable($settings['callback'])) { if (is_array($settings['callback'])) { $reflection = new \ReflectionMethod($settings['callback'][0], $settings['callback'][1]); } elseif (strpos($settings['callback'], '::') !== false) { $parts = explode('::', $settings['callback']); $reflection = new \ReflectionMethod($parts[0], $parts[1]); } else { $reflection = new \ReflectionMethod(null, $settings['callback']); } $pass = array(); foreach ($reflection->getParameters() as $param) { if (isset($args[$param->getName()])) { $pass[] = $args[$param->getName()]; } else { $pass[] = null; } } return $reflection->invokeArgs($this, $pass); } return false; }
/** * @param Form $form * @param $formHtml */ public function populateValuesWithLead(Form $form, &$formHtml) { $formName = $form->generateFormName(); $lead = $this->leadModel->getCurrentLead(); $fields = $form->getFields(); /** @var \Mautic\FormBundle\Entity\Field $f */ foreach ($fields as $f) { $leadField = $f->getLeadField(); $isAutoFill = $f->getIsAutoFill(); if (isset($leadField) && $isAutoFill) { $value = $lead->getFieldValue($leadField); if (!empty($value)) { $this->fieldHelper->populateField($f, $value, $formName, $formHtml); } } } }
/** * Generate the form's html * * @param Form $entity * @param bool $persist * * @return string */ public function generateHtml(Form $entity, $persist = true) { //generate cached HTML $theme = $entity->getTemplate(); $submissions = null; $lead = $this->leadModel->getCurrentLead(); if (!empty($theme)) { $theme .= '|'; } if ($entity->usesProgressiveProfiling()) { $submissions = $this->getRepository()->getFormResults($entity, ['leadId' => $lead->getId(), 'limit' => 200]); } $html = $this->templatingHelper->getTemplating()->render($theme . 'MauticFormBundle:Builder:form.html.php', ['form' => $entity, 'theme' => $theme, 'submissions' => $submissions, 'lead' => $lead]); if (!$entity->usesProgressiveProfiling()) { $entity->setCachedHtml($html); if ($persist) { //bypass model function as events aren't needed for this $this->getRepository()->saveEntity($entity); } } return $html; }
/** * @param Request $request * @param string $code * * @throws \Doctrine\ORM\ORMException * @throws \Exception */ public function hitVideo($request, $code = '200') { //don't skew results with in-house hits if (!$this->security->isAnonymous()) { //return; } $lead = $this->leadModel->getCurrentLead(); $guid = $request->get('guid'); $hit = $this->getHitForLeadByGuid($lead, $guid); $hit->setGuid($guid); $hit->setDateHit(new \Datetime()); $hit->setDuration($request->get('duration')); $hit->setUrl($request->get('url')); $hit->setTimeWatched($request->get('total_watched')); //check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $hit->setIpAddress($ipAddress); // Store query array $query = $request->query->all(); unset($query['d']); $hit->setQuery($query); $hit->setLead($lead); //glean info from the IP address if ($details = $ipAddress->getIpDetails()) { $hit->setCountry($details['country']); $hit->setRegion($details['region']); $hit->setCity($details['city']); $hit->setIsp($details['isp']); $hit->setOrganization($details['organization']); } $hit->setCode($code); if (!$hit->getReferer()) { $hit->setReferer($request->server->get('HTTP_REFERER')); } $hit->setUserAgent($request->server->get('HTTP_USER_AGENT')); $hit->setRemoteHost($request->server->get('REMOTE_HOST')); //get a list of the languages the user prefers $browserLanguages = $request->server->get('HTTP_ACCEPT_LANGUAGE'); if (!empty($browserLanguages)) { $languages = explode(',', $browserLanguages); foreach ($languages as $k => $l) { if ($pos = strpos(';q=', $l) !== false) { //remove weights $languages[$k] = substr($l, 0, $pos); } } $hit->setBrowserLanguages($languages); } // Wrap in a try/catch to prevent deadlock errors on busy servers try { $this->em->persist($hit); $this->em->flush($hit); } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } if ($this->dispatcher->hasListeners(PageEvents::VIDEO_ON_HIT)) { $event = new VideoHitEvent($hit, $request, $code); $this->dispatcher->dispatch(PageEvents::VIDEO_ON_HIT, $event); } }
/** * Initialize the QueryBuilder object to generate reports from. * * @param ReportGraphEvent $event */ public function onReportGraphGenerate(ReportGraphEvent $event) { // Context check, we only want to fire for Lead reports if (!$event->checkContext(['leads', 'lead.pointlog', 'contact.attribution.multi'])) { return; } $graphs = $event->getRequestedGraphs(); $qb = $event->getQueryBuilder(); $pointLogRepo = $this->leadModel->getPointLogRepository(); foreach ($graphs as $g) { $queryBuilder = clone $qb; $options = $event->getOptions($g); /** @var ChartQuery $chartQuery */ $chartQuery = clone $options['chartQuery']; $attributionQb = clone $queryBuilder; $chartQuery->applyDateFilters($queryBuilder, 'date_added', 'l'); switch ($g) { case 'mautic.lead.graph.pie.attribution_stages': case 'mautic.lead.graph.pie.attribution_campaigns': case 'mautic.lead.graph.pie.attribution_actions': case 'mautic.lead.graph.pie.attribution_channels': $attributionQb->resetQueryParts(['select', 'orderBy']); $outerQb = clone $attributionQb; $outerQb->resetQueryParts()->select('slice, sum(contact_attribution) as total_attribution')->groupBy('slice'); $groupBy = str_replace('mautic.lead.graph.pie.attribution_', '', $g); switch ($groupBy) { case 'stages': $attributionQb->select('CONCAT_WS(\':\', s.id, s.name) as slice, l.attribution as contact_attribution')->groupBy('l.id, s.id'); break; case 'campaigns': $attributionQb->select('CONCAT_WS(\':\', c.id, c.name) as slice, l.attribution as contact_attribution')->groupBy('l.id, c.id'); break; case 'actions': $attributionQb->select('SUBSTRING_INDEX(e.type, \'.\', -1) as slice, l.attribution as contact_attribution')->groupBy('l.id, SUBSTRING_INDEX(e.type, \'.\', -1)'); break; case 'channels': $attributionQb->select('SUBSTRING_INDEX(e.type, \'.\', 1) as slice, l.attribution as contact_attribution')->groupBy('l.id, SUBSTRING_INDEX(e.type, \'.\', 1)'); break; } $outerQb->from(sprintf('(%s) subq', $attributionQb->getSQL())); $outerQb->setParameters($attributionQb->getParameters()); $chart = new PieChart(); $data = $outerQb->execute()->fetchAll(); foreach ($data as $row) { switch ($groupBy) { case 'actions': $label = $this->channelActions[$row['slice']]; break; case 'channels': $label = $this->channels[$row['slice']]; break; default: $label = empty($row['slice']) ? $this->translator->trans('mautic.core.none') : $row['slice']; } $chart->setDataset($label, $row['total_attribution']); } $event->setGraph($g, ['data' => $chart->render(), 'name' => $g, 'iconClass' => 'fa-dollar']); break; case 'mautic.lead.graph.line.leads': $chart = new LineChart(null, $options['dateFrom'], $options['dateTo']); $chartQuery->modifyTimeDataQuery($queryBuilder, 'date_added', 'l'); $leads = $chartQuery->loadAndBuildTimeData($queryBuilder); $chart->setDataset($options['translator']->trans('mautic.lead.all.leads'), $leads); $queryBuilder->andwhere($qb->expr()->isNotNull('l.date_identified')); $identified = $chartQuery->loadAndBuildTimeData($queryBuilder); $chart->setDataset($options['translator']->trans('mautic.lead.identified'), $identified); $data = $chart->render(); $data['name'] = $g; $event->setGraph($g, $data); break; case 'mautic.lead.graph.line.points': $chart = new LineChart(null, $options['dateFrom'], $options['dateTo']); $chartQuery->modifyTimeDataQuery($queryBuilder, 'date_added', 'lp'); $leads = $chartQuery->loadAndBuildTimeData($queryBuilder); $chart->setDataset($options['translator']->trans('mautic.lead.graph.line.points'), $leads); $data = $chart->render(); $data['name'] = $g; $event->setGraph($g, $data); break; case 'mautic.lead.table.most.points': $queryBuilder->select('l.id, l.email as title, sum(lp.delta) as points')->groupBy('l.id, l.email')->orderBy('points', 'DESC'); $limit = 10; $offset = 0; $items = $pointLogRepo->getMostPoints($queryBuilder, $limit, $offset); $graphData = []; $graphData['data'] = $items; $graphData['name'] = $g; $graphData['iconClass'] = 'fa-asterisk'; $graphData['link'] = 'mautic_contact_action'; $event->setGraph($g, $graphData); break; case 'mautic.lead.table.top.countries': $queryBuilder->select('l.country as title, count(l.country) as quantity')->groupBy('l.country')->orderBy('quantity', 'DESC'); $limit = 10; $offset = 0; $items = $pointLogRepo->getMostLeads($queryBuilder, $limit, $offset); $graphData = []; $graphData['data'] = $items; $graphData['name'] = $g; $graphData['iconClass'] = 'fa-globe'; $event->setGraph($g, $graphData); break; case 'mautic.lead.table.top.cities': $queryBuilder->select('l.city as title, count(l.city) as quantity')->groupBy('l.city')->orderBy('quantity', 'DESC'); $limit = 10; $offset = 0; $items = $pointLogRepo->getMostLeads($queryBuilder, $limit, $offset); $graphData = []; $graphData['data'] = $items; $graphData['name'] = $g; $graphData['iconClass'] = 'fa-university'; $event->setGraph($g, $graphData); break; case 'mautic.lead.table.top.events': $queryBuilder->select('lp.event_name as title, count(lp.event_name) as events')->groupBy('lp.event_name')->orderBy('events', 'DESC'); $limit = 10; $offset = 0; $items = $pointLogRepo->getMostPoints($queryBuilder, $limit, $offset); $graphData = []; $graphData['data'] = $items; $graphData['name'] = $g; $graphData['iconClass'] = 'fa-calendar'; $event->setGraph($g, $graphData); break; case 'mautic.lead.table.top.actions': $queryBuilder->select('lp.action_name as title, count(lp.action_name) as actions')->groupBy('lp.action_name')->orderBy('actions', 'DESC'); $limit = 10; $offset = 0; $items = $pointLogRepo->getMostPoints($queryBuilder, $limit, $offset); $graphData = []; $graphData['data'] = $items; $graphData['name'] = $g; $graphData['iconClass'] = 'fa-bolt'; $event->setGraph($g, $graphData); break; } unset($queryBuilder); } }
/** * @param MauticEvents\CommandListEvent $event */ public function onBuildCommandList(MauticEvents\CommandListEvent $event) { if ($this->security->isGranted(['lead:leads:viewown', 'lead:leads:viewother'], 'MATCH_ONE')) { $event->addCommands('mautic.lead.leads', $this->leadModel->getCommandList()); } }
/** * @param $queue * * @return int */ public function processMessageQueue($queue) { if (!is_array($queue)) { if (!$queue instanceof MessageQueue) { throw new \InvalidArgumentException('$queue must be an instance of ' . MessageQueue::class); } $queue = [$queue->getId() => $queue]; } $counter = 0; $contacts = []; $byChannel = []; // Lead entities will not have profile fields populated due to the custom field use - therefore to optimize resources, // get a list of leads to fetch details all at once along with company details for dynamic email content, etc /** @var MessageQueue $message */ foreach ($queue as $message) { $contacts[$message->getId()] = $message->getLead()->getId(); } $contactData = $this->leadModel->getRepository()->getContacts($contacts); $companyData = $this->companyModel->getRepository()->getCompaniesForContacts($contacts); foreach ($contacts as $messageId => $contactId) { $contactData[$contactId]['companies'] = $companyData[$contactId]; $queue[$messageId]->getLead()->setFields($contactData[$contactId]); } // Group queue by channel and channel ID - this make it possible for processing listeners to batch process such as // sending emails in batches to 3rd party transactional services via HTTP APIs foreach ($queue as $key => $message) { if (MessageQueue::STATUS_SENT == $message->getStatus()) { unset($queue[$key]); continue; } $messageChannel = $message->getChannel(); $messageChannelId = $message->getChannelId(); if (!$messageChannelId) { $messageChannelId = 0; } if (!isset($byChannel[$messageChannel])) { $byChannel[$messageChannel] = []; } if (!isset($byChannel[$messageChannel][$messageChannelId])) { $byChannel[$messageChannel][$messageChannelId] = []; } $byChannel[$messageChannel][$messageChannelId][] = $message; } // First try to batch process each channel foreach ($byChannel as $messageChannel => $channelMessages) { foreach ($channelMessages as $messageChannelId => $messages) { $event = new MessageQueueBatchProcessEvent($messages, $messageChannel, $messageChannelId); $ignore = null; $this->dispatchEvent('process_batch_message_queue', $ignore, false, $event); } } unset($byChannel); // Now check to see if the message was processed by the listener and if not // send it through a single process event listener foreach ($queue as $message) { if (!$message->isProcessed()) { $event = new MessageQueueProcessEvent($message); $this->dispatchEvent('process_message_queue', $message, false, $event); } if ($message->isSuccess()) { ++$counter; $message->setSuccess(); $message->setLastAttempt(new \DateTime()); $message->setDateSent(new \DateTime()); $message->setStatus(MessageQueue::STATUS_SENT); } elseif ($message->isFailed()) { // Failure such as email delivery issue or something so retry in a short time $this->rescheduleMessage($message, '15M'); } // otherwise assume the listener did something such as rescheduling the message } //add listener $this->saveEntities($queue); return $counter; }
/** * Create/update lead from form submit. * * @param $form * @param array $leadFieldMatches * * @return Lead */ protected function createLeadFromSubmit($form, array $leadFieldMatches, $leadFields) { //set the mapped data $inKioskMode = $form->isInKioskMode(); if (!$inKioskMode) { // Default to currently tracked lead $lead = $this->leadModel->getCurrentLead(); $leadId = $lead->getId(); $currentFields = $this->leadModel->flattenFields($lead->getFields()); $this->logger->debug('FORM: Not in kiosk mode so using current contact ID #' . $lead->getId()); } else { // Default to a new lead in kiosk mode $lead = new Lead(); $lead->setNewlyCreated(true); $currentFields = $leadFieldMatches; $leadId = null; $this->logger->debug('FORM: In kiosk mode so assuming a new contact'); } $uniqueLeadFields = $this->leadFieldModel->getUniqueIdentiferFields(); // Closure to get data and unique fields $getData = function ($currentFields, $uniqueOnly = false) use($leadFields, $uniqueLeadFields) { $uniqueFieldsWithData = $data = []; foreach ($leadFields as $alias => $properties) { $data[$alias] = ''; if (isset($currentFields[$alias])) { $value = $currentFields[$alias]; $data[$alias] = $value; // make sure the value is actually there and the field is one of our uniques if (!empty($value) && array_key_exists($alias, $uniqueLeadFields)) { $uniqueFieldsWithData[$alias] = $value; } } } return $uniqueOnly ? $uniqueFieldsWithData : [$data, $uniqueFieldsWithData]; }; // Closure to help search for a conflict $checkForIdentifierConflict = function ($fieldSet1, $fieldSet2) { // Find fields in both sets $potentialConflicts = array_keys(array_intersect_key($fieldSet1, $fieldSet2)); $this->logger->debug('FORM: Potential conflicts ' . implode(', ', array_keys($potentialConflicts)) . ' = ' . implode(', ', $potentialConflicts)); $conflicts = []; foreach ($potentialConflicts as $field) { if (!empty($fieldSet1[$field]) && !empty($fieldSet2[$field])) { if (strtolower($fieldSet1[$field]) !== strtolower($fieldSet2[$field])) { $conflicts[] = $field; } } } return [count($conflicts), $conflicts]; }; // Get data for the form submission list($data, $uniqueFieldsWithData) = $getData($leadFieldMatches); $this->logger->debug('FORM: Unique fields submitted include ' . implode(', ', $uniqueFieldsWithData)); // Check for duplicate lead /** @var \Mautic\LeadBundle\Entity\Lead[] $leads */ $leads = !empty($uniqueFieldsWithData) ? $this->em->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields($uniqueFieldsWithData, $leadId) : []; $uniqueFieldsCurrent = $getData($currentFields, true); if (count($leads)) { $this->logger->debug(count($leads) . ' found based on unique identifiers'); /** @var \Mautic\LeadBundle\Entity\Lead $foundLead */ $foundLead = $leads[0]; $this->logger->debug('FORM: Testing contact ID# ' . $foundLead->getId() . ' for conflicts'); // Check for a conflict with the currently tracked lead $foundLeadFields = $this->leadModel->flattenFields($foundLead->getFields()); // Get unique identifier fields for the found lead then compare with the lead currently tracked $uniqueFieldsFound = $getData($foundLeadFields, true); list($hasConflict, $conflicts) = $checkForIdentifierConflict($uniqueFieldsFound, $uniqueFieldsCurrent); if ($inKioskMode || $hasConflict) { // Use the found lead without merging because there is some sort of conflict with unique identifiers or in kiosk mode and thus should not merge $lead = $foundLead; if ($hasConflict) { $this->logger->debug('FORM: Conflicts found in ' . implode(', ', $conflicts) . ' so not merging'); } else { $this->logger->debug('FORM: In kiosk mode so not merging'); } } else { $this->logger->debug('FORM: Merging contacts ' . $lead->getId() . ' and ' . $foundLead->getId()); // Merge the found lead with currently tracked lead $lead = $this->leadModel->mergeLeads($lead, $foundLead); } // Update unique fields data for comparison with submitted data $currentFields = $this->leadModel->flattenFields($lead->getFields()); $uniqueFieldsCurrent = $getData($currentFields, true); } if (!$inKioskMode) { // Check for conflicts with the submitted data and the currently tracked lead list($hasConflict, $conflicts) = $checkForIdentifierConflict($uniqueFieldsWithData, $uniqueFieldsCurrent); $this->logger->debug('FORM: Current unique contact fields ' . implode(', ', array_keys($uniqueFieldsCurrent)) . ' = ' . implode(', ', $uniqueFieldsCurrent)); $this->logger->debug('FORM: Submitted unique contact fields ' . implode(', ', array_keys($uniqueFieldsWithData)) . ' = ' . implode(', ', $uniqueFieldsWithData)); if ($hasConflict) { // There's a conflict so create a new lead $lead = new Lead(); $lead->setNewlyCreated(true); $this->logger->debug('FORM: Conflicts found in ' . implode(', ', $conflicts) . ' between current tracked contact and submitted data so assuming a new contact'); } } //check for existing IP address $ipAddress = $this->ipLookupHelper->getIpAddress(); //no lead was found by a mapped email field so create a new one if ($lead->isNewlyCreated()) { if (!$inKioskMode) { $lead->addIpAddress($ipAddress); $this->logger->debug('FORM: Associating ' . $ipAddress->getIpAddress() . ' to contact'); } } elseif (!$inKioskMode) { $leadIpAddresses = $lead->getIpAddresses(); if (!$leadIpAddresses->contains($ipAddress)) { $lead->addIpAddress($ipAddress); $this->logger->debug('FORM: Associating ' . $ipAddress->getIpAddress() . ' to contact'); } } //set the mapped fields $this->leadModel->setFieldValues($lead, $data, false); if (!empty($event)) { $event->setIpAddress($ipAddress); $lead->addPointsChangeLog($event); } // last active time $lead->setLastActive(new \DateTime()); //create a new lead $this->leadModel->saveEntity($lead, false); if (!$inKioskMode) { // Set the current lead which will generate tracking cookies $this->leadModel->setCurrentLead($lead); } else { // Set system current lead which will still allow execution of events without generating tracking cookies $this->leadModel->setSystemCurrentLead($lead); } return $lead; }
/** * Triggers a specific point change * * @param $type * @param mixed $eventDetails passthrough from function triggering action to the callback function * @param mixed $typeId Something unique to the triggering event to prevent unnecessary duplicate calls * @param Lead $lead *25 * @return void */ public function triggerAction($type, $eventDetails = null, $typeId = null, Lead $lead = null) { //only trigger actions for anonymous users if (!$this->security->isAnonymous()) { return; } if ($typeId !== null && MAUTIC_ENV === 'prod') { //let's prevent some unnecessary DB calls $triggeredEvents = $this->session->get('mautic.triggered.point.actions', array()); if (in_array($typeId, $triggeredEvents)) { return; } $triggeredEvents[] = $typeId; $this->session->set('mautic.triggered.point.actions', $triggeredEvents); } //find all the actions for published points /** @var \Mautic\PointBundle\Entity\PointRepository $repo */ $repo = $this->getRepository(); $availablePoints = $repo->getPublishedByType($type); $ipAddress = $this->ipLookupHelper->getIpAddress(); if (null === $lead) { $lead = $this->leadModel->getCurrentLead(); if (null === $lead || !$lead->getId()) { return; } } //get available actions $availableActions = $this->getPointActions(); //get a list of actions that has already been performed on this lead $completedActions = $repo->getCompletedLeadActions($type, $lead->getId()); $persist = array(); foreach ($availablePoints as $action) { //if it's already been done, then skip it if (isset($completedActions[$action->getId()])) { continue; } //make sure the action still exists if (!isset($availableActions['actions'][$action->getType()])) { continue; } $settings = $availableActions['actions'][$action->getType()]; $args = array('action' => array('id' => $action->getId(), 'type' => $action->getType(), 'name' => $action->getName(), 'properties' => $action->getProperties(), 'points' => $action->getDelta()), 'lead' => $lead, 'factory' => $this->factory, 'eventDetails' => $eventDetails); $callback = isset($settings['callback']) ? $settings['callback'] : array('\\Mautic\\PointBundle\\Helper\\EventHelper', 'engagePointAction'); if (is_callable($callback)) { if (is_array($callback)) { $reflection = new \ReflectionMethod($callback[0], $callback[1]); } elseif (strpos($callback, '::') !== false) { $parts = explode('::', $callback); $reflection = new \ReflectionMethod($parts[0], $parts[1]); } else { $reflection = new \ReflectionMethod(null, $callback); } $pass = array(); foreach ($reflection->getParameters() as $param) { if (isset($args[$param->getName()])) { $pass[] = $args[$param->getName()]; } else { $pass[] = null; } } $pointsChange = $reflection->invokeArgs($this, $pass); if ($pointsChange) { $delta = $action->getDelta(); $lead->adjustPoints($delta); $parsed = explode('.', $action->getType()); $lead->addPointsChangeLogEntry($parsed[0], $action->getId() . ": " . $action->getName(), $parsed[1], $delta, $ipAddress); $event = new PointActionEvent($action, $lead); $this->dispatcher->dispatch(PointEvents::POINT_ON_ACTION, $event); $log = new LeadPointLog(); $log->setIpAddress($ipAddress); $log->setPoint($action); $log->setLead($lead); $log->setDateFired(new \DateTime()); $persist[] = $log; } } } if (!empty($persist)) { $this->leadModel->saveEntity($lead); $this->getRepository()->saveEntities($persist); // Detach logs to reserve memory $this->em->clear('Mautic\\PointBundle\\Entity\\LeadPointLog'); } }
/** * Record page hit. * * @param $page * @param Request $request * @param string $code * @param Lead|null $lead * @param array $query * * @return Hit $hit * * @throws \Exception */ public function hitPage($page, Request $request, $code = '200', Lead $lead = null, $query = []) { // Don't skew results with user hits if (!$this->security->isAnonymous()) { return; } // Process the query if (empty($query)) { $query = $this->getHitQuery($request, $page); } $hit = new Hit(); $hit->setDateHit(new \Datetime()); // Check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $hit->setIpAddress($ipAddress); // Check for any clickthrough info $clickthrough = []; if (!empty($query['ct'])) { $clickthrough = $query['ct']; if (!is_array($clickthrough)) { $clickthrough = $this->decodeArrayFromUrl($clickthrough); } if (!empty($clickthrough['channel'])) { if (count($clickthrough['channel']) === 1) { $channelId = reset($clickthrough['channel']); $channel = key($clickthrough['channel']); } else { $channel = $clickthrough['channel'][0]; $channelId = (int) $clickthrough['channel'][1]; } $hit->setSource($channel); $hit->setSourceId($channelId); } elseif (!empty($clickthrough['source'])) { $hit->setSource($clickthrough['source'][0]); $hit->setSourceId($clickthrough['source'][1]); } if (!empty($clickthrough['email'])) { $emailRepo = $this->em->getRepository('MauticEmailBundle:Email'); if ($emailEntity = $emailRepo->getEntity($clickthrough['email'])) { $hit->setEmail($emailEntity); } } } // Get lead if required if (null == $lead) { $lead = $this->leadModel->getContactFromRequest($query); } $this->leadModel->saveEntity($lead); // Set info from request $hit->setQuery($query); $hit->setUrl(isset($query['page_url']) ? $query['page_url'] : $request->getRequestUri()); if (isset($query['page_referrer'])) { $hit->setReferer($query['page_referrer']); } if (isset($query['page_language'])) { $hit->setPageLanguage($query['page_language']); } if (isset($query['page_title'])) { $hit->setUrlTitle($query['page_title']); } // Store tracking ID list($trackingId, $trackingNewlyGenerated) = $this->leadModel->getTrackingCookie(); $hit->setTrackingId($trackingId); $hit->setLead($lead); $isUnique = $trackingNewlyGenerated; if (!$trackingNewlyGenerated) { $lastHit = $request->cookies->get('mautic_referer_id'); if (!empty($lastHit)) { //this is not a new session so update the last hit if applicable with the date/time the user left $this->getHitRepository()->updateHitDateLeft($lastHit); } // Check if this is a unique page hit $isUnique = $this->getHitRepository()->isUniquePageHit($page, $trackingId); } if (!empty($page)) { if ($page instanceof Page) { $hit->setPage($page); $hit->setPageLanguage($page->getLanguage()); $isVariant = $isUnique ? $page->getVariantStartDate() : false; try { $this->getRepository()->upHitCount($page->getId(), 1, $isUnique, !empty($isVariant)); } catch (\Exception $exception) { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } elseif ($page instanceof Redirect) { $hit->setRedirect($page); try { $this->pageRedirectModel->getRepository()->upHitCount($page->getId(), 1, $isUnique); // If this is a trackable, up the trackable counts as well if (!empty($clickthrough['channel'])) { $channelId = reset($clickthrough['channel']); $channel = key($clickthrough['channel']); $this->pageTrackableModel->getRepository()->upHitCount($page->getId(), $channel, $channelId, 1, $isUnique); } } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } } } //glean info from the IP address if ($details = $ipAddress->getIpDetails()) { $hit->setCountry($details['country']); $hit->setRegion($details['region']); $hit->setCity($details['city']); $hit->setIsp($details['isp']); $hit->setOrganization($details['organization']); } $hit->setCode($code); if (!$hit->getReferer()) { $hit->setReferer($request->server->get('HTTP_REFERER')); } $hit->setUserAgent($request->server->get('HTTP_USER_AGENT')); $hit->setRemoteHost($request->server->get('REMOTE_HOST')); if ($isUnique) { // Add UTM tags entry if a UTM tag exist $queryHasUtmTags = false; if (!is_array($query)) { parse_str($query, $query); } foreach ($query as $key => $value) { if (strpos($key, 'utm_') !== false) { $queryHasUtmTags = true; break; } } if ($queryHasUtmTags) { $utmTags = new UtmTag(); $utmTags->setDateAdded($hit->getDateHit()); $utmTags->setUrl($hit->getUrl()); $utmTags->setReferer($hit->getReferer()); $utmTags->setQuery($hit->getQuery()); $utmTags->setUserAgent($hit->getUserAgent()); $utmTags->setRemoteHost($hit->getRemoteHost()); $utmTags->setLead($lead); if (key_exists('utm_campaign', $query)) { $utmTags->setUtmCampaign($query['utm_campaign']); } if (key_exists('utm_term', $query)) { $utmTags->setUtmTerm($query['utm_term']); } if (key_exists('utm_content', $query)) { $utmTags->setUtmConent($query['utm_content']); } if (key_exists('utm_medium', $query)) { $utmTags->setUtmMedium($query['utm_medium']); } if (key_exists('utm_source', $query)) { $utmTags->setUtmSource($query['utm_source']); } $repo = $this->em->getRepository('MauticLeadBundle:UtmTag'); $repo->saveEntity($utmTags); $this->leadModel->setUtmTags($lead, $utmTags); } } //get a list of the languages the user prefers $browserLanguages = $request->server->get('HTTP_ACCEPT_LANGUAGE'); if (!empty($browserLanguages)) { $languages = explode(',', $browserLanguages); foreach ($languages as $k => $l) { if ($pos = strpos(';q=', $l) !== false) { //remove weights $languages[$k] = substr($l, 0, $pos); } } $hit->setBrowserLanguages($languages); } //device granularity $dd = new DeviceDetector($request->server->get('HTTP_USER_AGENT')); $dd->parse(); $deviceRepo = $this->leadModel->getDeviceRepository(); $device = $deviceRepo->getDevice(null, $lead, $dd->getDeviceName(), $dd->getBrand(), $dd->getModel()); if (empty($device)) { $device = new LeadDevice(); $device->setClientInfo($dd->getClient()); $device->setDevice($dd->getDeviceName()); $device->setDeviceBrand($dd->getBrand()); $device->setDeviceModel($dd->getModel()); $device->setDeviceOs($dd->getOs()); $device->setDateOpen($hit->getDateHit()); $device->setLead($lead); $this->em->persist($device); } else { $device = $deviceRepo->getEntity($device['id']); } $hit->setDeviceStat($device); // Wrap in a try/catch to prevent deadlock errors on busy servers try { $this->em->persist($hit); $this->em->flush($hit); } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } if ($this->dispatcher->hasListeners(PageEvents::PAGE_ON_HIT)) { $event = new PageHitEvent($hit, $request, $code, $clickthrough, $isUnique); $this->dispatcher->dispatch(PageEvents::PAGE_ON_HIT, $event); } //save hit to the cookie to use to update the exit time $this->cookieHelper->setCookie('mautic_referer_id', $hit->getId()); return $hit; }
/** * Set a widget detail when needed. * * @param WidgetDetailEvent $event */ public function onWidgetDetailGenerate(WidgetDetailEvent $event) { $this->checkPermissions($event); $canViewOthers = $event->hasPermission('form:forms:viewother'); if ($event->getType() == 'created.leads.in.time') { $widget = $event->getWidget(); $params = $widget->getParams(); if (isset($params['flag'])) { $params['filter']['flag'] = $params['flag']; } if (!$event->isCached()) { $event->setTemplateData(['chartType' => 'line', 'chartHeight' => $widget->getHeight() - 80, 'chartData' => $this->leadModel->getLeadsLineChartData($params['timeUnit'], $params['dateFrom'], $params['dateTo'], $params['dateFormat'], $params['filter'], $canViewOthers)]); } $event->setTemplate('MauticCoreBundle:Helper:chart.html.php'); $event->stopPropagation(); return; } if ($event->getType() == 'anonymous.vs.identified.leads') { if (!$event->isCached()) { $params = $event->getWidget()->getParams(); $event->setTemplateData(['chartType' => 'pie', 'chartHeight' => $event->getWidget()->getHeight() - 80, 'chartData' => $this->leadModel->getAnonymousVsIdentifiedPieChartData($params['dateFrom'], $params['dateTo'], $canViewOthers)]); } $event->setTemplate('MauticCoreBundle:Helper:chart.html.php'); $event->stopPropagation(); return; } if ($event->getType() == 'map.of.leads') { if (!$event->isCached()) { $params = $event->getWidget()->getParams(); $event->setTemplateData(['height' => $event->getWidget()->getHeight() - 80, 'data' => $this->leadModel->getLeadMapData($params['dateFrom'], $params['dateTo'], $canViewOthers)]); } $event->setTemplate('MauticCoreBundle:Helper:map.html.php'); $event->stopPropagation(); return; } if ($event->getType() == 'top.lists') { if (!$event->isCached()) { $params = $event->getWidget()->getParams(); if (empty($params['limit'])) { // Count the list limit from the widget height $limit = round(($event->getWidget()->getHeight() - 80) / 35 - 1); } else { $limit = $params['limit']; } $lists = $this->leadListModel->getTopLists($limit, $params['dateFrom'], $params['dateTo'], $canViewOthers); $items = []; // Build table rows with links if ($lists) { foreach ($lists as &$list) { $listUrl = $this->router->generate('mautic_segment_action', ['objectAction' => 'edit', 'objectId' => $list['id']]); $row = [['value' => $list['name'], 'type' => 'link', 'link' => $listUrl], ['value' => $list['leads']]]; $items[] = $row; } } $event->setTemplateData(['headItems' => [$event->getTranslator()->trans('mautic.dashboard.label.title'), $event->getTranslator()->trans('mautic.lead.leads')], 'bodyItems' => $items, 'raw' => $lists]); } $event->setTemplate('MauticCoreBundle:Helper:table.html.php'); $event->stopPropagation(); return; } if ($event->getType() == 'lead.lifetime') { $params = $event->getWidget()->getParams(); if (empty($params['limit'])) { // Count the list limit from the widget height $limit = round(($event->getWidget()->getHeight() - 80) / 35 - 1); } else { $limit = $params['limit']; } $maxSegmentsToshow = 4; $params['filter']['flag'] = []; if (isset($params['flag'])) { $params['filter']['flag'] = $params['flag']; $maxSegmentsToshow = count($params['filter']['flag']); } $lists = $this->leadListModel->getLifeCycleSegments($maxSegmentsToshow, $params['dateFrom'], $params['dateTo'], $canViewOthers, $params['filter']['flag']); $items = []; if (empty($lists)) { $lists[] = ['leads' => 0, 'id' => 0, 'name' => $event->getTranslator()->trans('mautic.lead.all.leads'), 'alias' => '']; } // Build table rows with links if ($lists) { $stages = []; $deviceGranularity = []; foreach ($lists as &$list) { if ($list['alias'] != '') { $listUrl = $this->router->generate('mautic_contact_index', ['search' => 'segment:' . $list['alias']]); } else { $listUrl = $this->router->generate('mautic_contact_index', []); } if ($list['id']) { $params['filter']['leadlist_id'] = ['value' => $list['id'], 'list_column_name' => 't.id']; } else { unset($params['filter']['leadlist_id']); } $column = $this->leadListModel->getLifeCycleSegmentChartData($params['timeUnit'], $params['dateFrom'], $params['dateTo'], $params['dateFormat'], $params['filter'], $canViewOthers, $list['name']); $items['columnName'][] = $list['name']; $items['value'][] = $list['leads']; $items['link'][] = $listUrl; $items['chartItems'][] = $column; $stages[] = $this->leadListModel->getStagesBarChartData($params['timeUnit'], $params['dateFrom'], $params['dateTo'], $params['dateFormat'], $params['filter'], $canViewOthers); $deviceGranularity[] = $this->leadListModel->getDeviceGranularityData($params['timeUnit'], $params['dateFrom'], $params['dateTo'], $params['dateFormat'], $params['filter'], $canViewOthers); } $width = 100 / count($lists); $event->setTemplateData(['columnName' => $items['columnName'], 'value' => $items['value'], 'width' => $width, 'link' => $items['link'], 'chartType' => 'pie', 'chartHeight' => $event->getWidget()->getHeight() - 180, 'chartItems' => $items['chartItems'], 'stages' => $stages, 'devices' => $deviceGranularity]); $event->setTemplate('MauticCoreBundle:Helper:lifecycle.html.php'); $event->stopPropagation(); } return; } if ($event->getType() == 'top.owners') { if (!$canViewOthers) { $event->setErrorMessage($this->translator->trans('mautic.dashboard.missing.permission', ['%section%' => $this->bundle])); $event->stopPropagation(); return; } if (!$event->isCached()) { $params = $event->getWidget()->getParams(); if (empty($params['limit'])) { // Count the list limit from the widget height $limit = round(($event->getWidget()->getHeight() - 80) / 35 - 1); } else { $limit = $params['limit']; } $owners = $this->leadModel->getTopOwners($limit, $params['dateFrom'], $params['dateTo']); $items = []; // Build table rows with links if ($owners) { foreach ($owners as &$owner) { $ownerUrl = $this->router->generate('mautic_user_action', ['objectAction' => 'edit', 'objectId' => $owner['owner_id']]); $row = [['value' => $owner['first_name'] . ' ' . $owner['last_name'], 'type' => 'link', 'link' => $ownerUrl], ['value' => $owner['leads']]]; $items[] = $row; } } $event->setTemplateData(['headItems' => [$event->getTranslator()->trans('mautic.user.account.permissions.editname'), $event->getTranslator()->trans('mautic.lead.leads')], 'bodyItems' => $items, 'raw' => $owners]); } $event->setTemplate('MauticCoreBundle:Helper:table.html.php'); $event->stopPropagation(); return; } if ($event->getType() == 'top.creators') { if (!$canViewOthers) { $event->setErrorMessage($this->translator->trans('mautic.dashboard.missing.permission', ['%section%' => $this->bundle])); $event->stopPropagation(); return; } if (!$event->isCached()) { $params = $event->getWidget()->getParams(); if (empty($params['limit'])) { // Count the list limit from the widget height $limit = round(($event->getWidget()->getHeight() - 80) / 35 - 1); } else { $limit = $params['limit']; } $creators = $this->leadModel->getTopCreators($limit, $params['dateFrom'], $params['dateTo']); $items = []; // Build table rows with links if ($creators) { foreach ($creators as &$creator) { $creatorUrl = $this->router->generate('mautic_user_action', ['objectAction' => 'edit', 'objectId' => $creator['created_by']]); $row = [['value' => $creator['created_by_user'], 'type' => 'link', 'link' => $creatorUrl], ['value' => $creator['leads']]]; $items[] = $row; } } $event->setTemplateData(['headItems' => [$event->getTranslator()->trans('mautic.user.account.permissions.editname'), $event->getTranslator()->trans('mautic.lead.leads')], 'bodyItems' => $items, 'raw' => $creators]); } $event->setTemplate('MauticCoreBundle:Helper:table.html.php'); $event->stopPropagation(); return; } if ($event->getType() == 'created.leads') { if (!$event->isCached()) { $params = $event->getWidget()->getParams(); if (empty($params['limit'])) { // Count the leads limit from the widget height $limit = round(($event->getWidget()->getHeight() - 80) / 35 - 1); } else { $limit = $params['limit']; } $leads = $this->leadModel->getLeadList($limit, $params['dateFrom'], $params['dateTo'], $canViewOthers, [], ['canViewOthers' => $canViewOthers]); $items = []; if (empty($leads)) { $leads[] = ['name' => $this->translator->trans('mautic.report.report.noresults')]; } // Build table rows with links if ($leads) { foreach ($leads as &$lead) { $leadUrl = isset($lead['id']) ? $this->router->generate('mautic_contact_action', ['objectAction' => 'view', 'objectId' => $lead['id']]) : ''; $type = isset($lead['id']) ? 'link' : 'text'; $row = [['value' => $lead['name'], 'type' => $type, 'link' => $leadUrl]]; $items[] = $row; } } $event->setTemplateData(['headItems' => [$event->getTranslator()->trans('mautic.dashboard.label.title')], 'bodyItems' => $items, 'raw' => $leads]); } $event->setTemplate('MauticCoreBundle:Helper:table.html.php'); $event->stopPropagation(); return; } }
/** * @param $asset * @param null $request * @param string $code * @param array $systemEntry * * @throws \Doctrine\ORM\ORMException * @throws \Exception */ public function trackDownload($asset, $request = null, $code = '200', $systemEntry = []) { // Don't skew results with in-house downloads if (empty($systemEntry) && !$this->security->isAnonymous()) { return; } if ($request == null) { $request = $this->request; } $download = new Download(); $download->setDateDownload(new \Datetime()); // Download triggered by lead if (empty($systemEntry)) { //check for any clickthrough info $clickthrough = $request->get('ct', false); if (!empty($clickthrough)) { $clickthrough = $this->decodeArrayFromUrl($clickthrough); if (!empty($clickthrough['lead'])) { $lead = $this->leadModel->getEntity($clickthrough['lead']); if ($lead !== null) { $this->leadModel->setLeadCookie($clickthrough['lead']); list($trackingId, $trackingNewlyGenerated) = $this->leadModel->getTrackingCookie(); $leadClickthrough = true; $this->leadModel->setCurrentLead($lead); } } if (!empty($clickthrough['channel'])) { if (count($clickthrough['channel']) === 1) { $channelId = reset($clickthrough['channel']); $channel = key($clickthrough['channel']); } else { $channel = $clickthrough['channel'][0]; $channelId = (int) $clickthrough['channel'][1]; } $download->setSource($channel); $download->setSourceId($channelId); } elseif (!empty($clickthrough['source'])) { $download->setSource($clickthrough['source'][0]); $download->setSourceId($clickthrough['source'][1]); } if (!empty($clickthrough['email'])) { $emailRepo = $this->em->getRepository('MauticEmailBundle:Email'); if ($emailEntity = $emailRepo->getEntity($clickthrough['email'])) { $download->setEmail($emailEntity); } } } if (empty($leadClickthrough)) { list($lead, $trackingId, $trackingNewlyGenerated) = $this->leadModel->getCurrentLead(true); } $download->setLead($lead); } else { $trackingId = ''; if (isset($systemEntry['lead'])) { $lead = $systemEntry['lead']; if (!$lead instanceof Lead) { $leadId = is_array($lead) ? $lead['id'] : $lead; $lead = $this->em->getReference('MauticLeadBundle:Lead', $leadId); } $download->setLead($lead); } if (!empty($systemEntry['source'])) { $download->setSource($systemEntry['source'][0]); $download->setSourceId($systemEntry['source'][1]); } if (isset($systemEntry['email'])) { $email = $systemEntry['email']; if (!$email instanceof Email) { $emailId = is_array($email) ? $email['id'] : $email; $email = $this->em->getReference('MauticEmailBundle:Email', $emailId); } $download->setEmail($email); } if (isset($systemEntry['tracking_id'])) { $trackingId = $systemEntry['tracking_id']; $trackingNewlyGenerated = false; } elseif ($this->security->isAnonymous() && !defined('IN_MAUTIC_CONSOLE')) { // If the session is anonymous and not triggered via CLI, assume the lead did something to trigger the // system forced download such as an email list($trackingId, $trackingNewlyGenerated) = $this->leadModel->getTrackingCookie(); } } $isUnique = true; if (!empty($trackingNewlyGenerated)) { // Cookie was just generated so this is definitely a unique download $isUnique = $trackingNewlyGenerated; } elseif (!empty($trackingId)) { // Determine if this is a unique download $isUnique = $this->getDownloadRepository()->isUniqueDownload($asset->getId(), $trackingId); } $download->setTrackingId($trackingId); if (!empty($asset) && empty($systemEntry)) { $download->setAsset($asset); $this->getRepository()->upDownloadCount($asset->getId(), 1, $isUnique); } //check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $download->setCode($code); $download->setIpAddress($ipAddress); if ($request !== null) { $download->setReferer($request->server->get('HTTP_REFERER')); } // Dispatch event if ($this->dispatcher->hasListeners(AssetEvents::ASSET_ON_LOAD)) { $event = new AssetLoadEvent($download, $isUnique); $this->dispatcher->dispatch(AssetEvents::ASSET_ON_LOAD, $event); } // Wrap in a try/catch to prevent deadlock errors on busy servers try { $this->em->persist($download); $this->em->flush($download); } catch (\Exception $e) { if (MAUTIC_ENV === 'dev') { throw $e; } else { error_log($e); } } $this->em->detach($download); }