Ejemplo n.º 1
0
 /**
  * @param MauticEvents\GlobalSearchEvent $event
  */
 public function onGlobalSearch(MauticEvents\GlobalSearchEvent $event)
 {
     $str = $event->getSearchString();
     if (empty($str)) {
         return;
     }
     $anonymous = $this->translator->trans('mautic.lead.lead.searchcommand.isanonymous');
     $mine = $this->translator->trans('mautic.core.searchcommand.ismine');
     $filter = ['string' => $str, 'force' => ''];
     //only show results that are not anonymous so as to not clutter up things
     if (strpos($str, "{$anonymous}") === false) {
         $filter['force'] = " !{$anonymous}";
     }
     $permissions = $this->security->isGranted(['lead:leads:viewown', 'lead:leads:viewother'], 'RETURN_ARRAY');
     if ($permissions['lead:leads:viewown'] || $permissions['lead:leads:viewother']) {
         //only show own leads if the user does not have permission to view others
         if (!$permissions['lead:leads:viewother']) {
             $filter['force'] .= " {$mine}";
         }
         $results = $this->leadModel->getEntities(['limit' => 5, 'filter' => $filter, 'withTotalCount' => true]);
         $count = $results['count'];
         if ($count > 0) {
             $leads = $results['results'];
             $leadResults = [];
             foreach ($leads as $lead) {
                 $leadResults[] = $this->templating->renderResponse('MauticLeadBundle:SubscribedEvents\\Search:global.html.php', ['lead' => $lead])->getContent();
             }
             if ($results['count'] > 5) {
                 $leadResults[] = $this->templating->renderResponse('MauticLeadBundle:SubscribedEvents\\Search:global.html.php', ['showMore' => true, 'searchString' => $str, 'remaining' => $results['count'] - 5])->getContent();
             }
             $leadResults['count'] = $results['count'];
             $event->addResults('mautic.lead.leads', $leadResults);
         }
     }
 }
Ejemplo n.º 2
0
 /**
  * {@inheritdoc}
  *
  * @param  \Mautic\PointBundle\Entity\Trigger $entity
  * @param  bool                               $unlock
  */
 public function saveEntity($entity, $unlock = true)
 {
     $isNew = $entity->getId() ? false : true;
     parent::saveEntity($entity, $unlock);
     //should we trigger for existing leads?
     if ($entity->getTriggerExistingLeads() && $entity->isPublished()) {
         $events = $entity->getEvents();
         $repo = $this->getEventRepository();
         $persist = array();
         $ipAddress = $this->ipLookupHelper->getIpAddress();
         foreach ($events as $event) {
             $filter = array('force' => array(array('column' => 'l.date_added', 'expr' => 'lte', 'value' => (new DateTimeHelper($entity->getDateAdded()))->toUtcString()), array('column' => 'l.points', 'expr' => 'gte', 'value' => $entity->getPoints())));
             if (!$isNew) {
                 //get a list of leads that has already had this event applied
                 $leadIds = $repo->getLeadsForEvent($event->getId());
                 if (!empty($leadIds)) {
                     $filter['force'][] = array('column' => 'l.id', 'expr' => 'notIn', 'value' => $leadIds);
                 }
             }
             //get a list of leads that are before the trigger's date_added and trigger if not already done so
             $leads = $this->leadModel->getEntities(array('filter' => $filter));
             foreach ($leads as $l) {
                 if ($this->triggerEvent($event->convertToArray(), $l, true)) {
                     $log = new LeadTriggerLog();
                     $log->setIpAddress($ipAddress);
                     $log->setEvent($event);
                     $log->setLead($l);
                     $log->setDateFired(new \DateTime());
                     $event->addLog($log);
                     $persist[] = $event;
                 }
             }
         }
         if (!empty($persist)) {
             $repo->saveEntities($persist);
         }
     }
 }
Ejemplo n.º 3
0
 /**
  * Find and trigger the negative events, i.e. the events with a no decision path.
  *
  * @param Campaign        $campaign
  * @param int             $totalEventCount
  * @param int             $limit
  * @param bool            $max
  * @param OutputInterface $output
  * @param bool|false      $returnCounts    If true, returns array of counters
  *
  * @return int
  */
 public function triggerNegativeEvents($campaign, &$totalEventCount = 0, $limit = 100, $max = false, OutputInterface $output = null, $returnCounts = false)
 {
     defined('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED') or define('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED', 1);
     $this->logger->debug('CAMPAIGN: Triggering negative events');
     $campaignId = $campaign->getId();
     $campaignName = $campaign->getName();
     $repo = $this->getRepository();
     $campaignRepo = $this->getCampaignRepository();
     // Get events to avoid large number of joins
     $campaignEvents = $repo->getCampaignEvents($campaignId);
     // Get an array of events that are non-action based
     $nonActionEvents = [];
     $actionEvents = [];
     foreach ($campaignEvents as $id => $e) {
         if (!empty($e['decisionPath']) && !empty($e['parent_id']) && $campaignEvents[$e['parent_id']]['eventType'] != 'condition') {
             if ($e['decisionPath'] == 'no') {
                 $nonActionEvents[$e['parent_id']][$id] = $e;
             } elseif ($e['decisionPath'] == 'yes') {
                 $actionEvents[$e['parent_id']][] = $id;
             }
         }
     }
     $this->logger->debug('CAMPAIGN: Processing the children of the following events: ' . implode(', ', array_keys($nonActionEvents)));
     if (empty($nonActionEvents)) {
         // No non-action events associated with this campaign
         unset($campaignEvents);
         return 0;
     }
     // Get a count
     $leadCount = $campaignRepo->getCampaignLeadCount($campaignId);
     if ($output) {
         $output->writeln($this->translator->trans('mautic.campaign.trigger.lead_count_analyzed', ['%leads%' => $leadCount, '%batch%' => $limit]));
     }
     $start = $leadProcessedCount = $lastRoundPercentage = $executedEventCount = $evaluatedEventCount = $negativeExecutedCount = $negativeEvaluatedCount = 0;
     $nonActionEventCount = $leadCount * count($nonActionEvents);
     $eventSettings = $this->campaignModel->getEvents();
     $maxCount = $max ? $max : $nonActionEventCount;
     // Try to save some memory
     gc_enable();
     if ($leadCount) {
         if ($output) {
             $progress = ProgressBarHelper::init($output, $maxCount);
             $progress->start();
             if ($max) {
                 $progress->advance($totalEventCount);
             }
         }
         $sleepBatchCount = 0;
         $batchDebugCounter = 1;
         while ($start <= $leadCount) {
             $this->logger->debug('CAMPAIGN: Batch #' . $batchDebugCounter);
             // Get batched campaign ids
             $campaignLeads = $campaignRepo->getCampaignLeads($campaignId, $start, $limit, ['cl.lead_id, cl.date_added']);
             $campaignLeadIds = [];
             $campaignLeadDates = [];
             foreach ($campaignLeads as $r) {
                 $campaignLeadIds[] = $r['lead_id'];
                 $campaignLeadDates[$r['lead_id']] = $r['date_added'];
             }
             unset($campaignLeads);
             $this->logger->debug('CAMPAIGN: Processing the following contacts: ' . implode(', ', $campaignLeadIds));
             foreach ($nonActionEvents as $parentId => $events) {
                 // Just a check to ensure this is an appropriate action
                 if ($campaignEvents[$parentId]['eventType'] == 'action') {
                     $this->logger->debug('CAMPAIGN: Parent event ID #' . $parentId . ' is an action.');
                     continue;
                 }
                 // Get only leads who have had the action prior to the decision executed
                 $grandParentId = $campaignEvents[$parentId]['parent_id'];
                 // Get the lead log for this batch of leads limiting to those that have already triggered
                 // the decision's parent and haven't executed this level in the path yet
                 if ($grandParentId) {
                     $this->logger->debug('CAMPAIGN: Checking for contacts based on grand parent execution.');
                     $leadLog = $repo->getEventLog($campaignId, $campaignLeadIds, [$grandParentId], array_keys($events), true);
                     $applicableLeads = array_keys($leadLog);
                 } else {
                     $this->logger->debug('CAMPAIGN: Checking for contacts based on exclusion due to being at root level');
                     // The event has no grandparent (likely because the decision is first in the campaign) so find leads that HAVE
                     // already executed the events in the root level and exclude them
                     $havingEvents = isset($actionEvents[$parentId]) ? array_merge($actionEvents[$parentId], array_keys($events)) : array_keys($events);
                     $leadLog = $repo->getEventLog($campaignId, $campaignLeadIds, $havingEvents);
                     $unapplicableLeads = array_keys($leadLog);
                     // Only use leads that are not applicable
                     $applicableLeads = array_diff($campaignLeadIds, $unapplicableLeads);
                     unset($unapplicableLeads);
                 }
                 if (empty($applicableLeads)) {
                     $this->logger->debug('CAMPAIGN: No events are applicable');
                     continue;
                 }
                 $this->logger->debug('CAMPAIGN: These contacts have have not gone down the positive path: ' . implode(', ', $applicableLeads));
                 // Get the leads
                 $leads = $this->leadModel->getEntities(['filter' => ['force' => [['column' => 'l.id', 'expr' => 'in', 'value' => $applicableLeads]]], 'orderBy' => 'l.id', 'orderByDir' => 'asc']);
                 if (!count($leads)) {
                     // Just a precaution in case non-existent leads are lingering in the campaign leads table
                     $this->logger->debug('CAMPAIGN: No contact entities found.');
                     continue;
                 }
                 // Loop over the non-actions and determine if it has been processed for this lead
                 $leadDebugCounter = 1;
                 /** @var \Mautic\LeadBundle\Entity\Lead $lead */
                 foreach ($leads as $lead) {
                     ++$negativeEvaluatedCount;
                     // Set lead for listeners
                     $this->leadModel->setSystemCurrentLead($lead);
                     $this->logger->debug('CAMPAIGN: contact ID #' . $lead->getId() . '; #' . $leadDebugCounter . ' in batch #' . $batchDebugCounter);
                     // Prevent path if lead has already gone down this path
                     if (!isset($leadLog[$lead->getId()]) || !array_key_exists($parentId, $leadLog[$lead->getId()])) {
                         // Get date to compare against
                         $utcDateString = $grandParentId ? $leadLog[$lead->getId()][$grandParentId]['date_triggered'] : $campaignLeadDates[$lead->getId()];
                         // Convert to local DateTime
                         $grandParentDate = (new DateTimeHelper($utcDateString))->getLocalDateTime();
                         // Non-decision has not taken place yet, so cycle over each associated action to see if timing is right
                         $eventTiming = [];
                         $executeAction = false;
                         foreach ($events as $id => $e) {
                             if ($sleepBatchCount == $limit) {
                                 // Keep CPU down
                                 $this->batchSleep();
                                 $sleepBatchCount = 0;
                             } else {
                                 ++$sleepBatchCount;
                             }
                             if (isset($leadLog[$lead->getId()]) && array_key_exists($id, $leadLog[$lead->getId()])) {
                                 $this->logger->debug('CAMPAIGN: Event (ID #' . $id . ') has already been executed');
                                 unset($e);
                                 continue;
                             }
                             if (!isset($eventSettings[$e['eventType']][$e['type']])) {
                                 $this->logger->debug('CAMPAIGN: Event (ID #' . $id . ') no longer exists');
                                 unset($e);
                                 continue;
                             }
                             // First get the timing for all the 'non-decision' actions
                             $eventTiming[$id] = $this->checkEventTiming($e, $grandParentDate, true);
                             if ($eventTiming[$id] === true) {
                                 // Includes events to be executed now then schedule the rest if applicable
                                 $executeAction = true;
                             }
                             unset($e);
                         }
                         if (!$executeAction) {
                             $negativeEvaluatedCount += count($nonActionEvents);
                             // Timing is not appropriate so move on to next lead
                             unset($eventTiming);
                             continue;
                         }
                         if ($max && $totalEventCount + count($nonActionEvents) >= $max) {
                             // Hit the max or will hit the max while mid-process for the lead
                             if ($output) {
                                 $progress->finish();
                                 $output->writeln('');
                             }
                             $counts = ['events' => $nonActionEventCount, 'evaluated' => $negativeEvaluatedCount, 'executed' => $negativeExecutedCount, 'totalEvaluated' => $evaluatedEventCount, 'totalExecuted' => $executedEventCount];
                             $this->logger->debug('CAMPAIGN: Counts - ' . var_export($counts, true));
                             return $returnCounts ? $counts : $executedEventCount;
                         }
                         $decisionLogged = false;
                         // Execute or schedule events
                         $this->logger->debug('CAMPAIGN: Processing the following events for contact ID# ' . $lead->getId() . ': ' . implode(', ', array_keys($eventTiming)));
                         foreach ($eventTiming as $id => $eventTriggerDate) {
                             // Set event
                             $event = $events[$id];
                             $event['campaign'] = ['id' => $campaignId, 'name' => $campaignName];
                             // Set lead in case this is triggered by the system
                             $this->leadModel->setSystemCurrentLead($lead);
                             if ($this->executeEvent($event, $campaign, $lead, $eventSettings, false, null, $eventTriggerDate, false, $evaluatedEventCount, $executedEventCount, $totalEventCount)) {
                                 if (!$decisionLogged) {
                                     // Log the decision
                                     $log = $this->getLogEntity($parentId, $campaign, $lead, null, true);
                                     $log->setDateTriggered(new \DateTime());
                                     $log->setNonActionPathTaken(true);
                                     $repo->saveEntity($log);
                                     $this->em->detach($log);
                                     unset($log);
                                     $decisionLogged = true;
                                 }
                                 ++$negativeExecutedCount;
                             }
                             unset($utcDateString, $grandParentDate);
                         }
                     } else {
                         $this->logger->debug('CAMPAIGN: Decision has already been executed.');
                     }
                     $currentCount = $max ? $totalEventCount : $negativeEvaluatedCount;
                     if ($output && $currentCount < $maxCount) {
                         $progress->setProgress($currentCount);
                     }
                     ++$leadDebugCounter;
                     // Save RAM
                     $this->em->detach($lead);
                     unset($lead);
                 }
             }
             // Next batch
             $start += $limit;
             // Save RAM
             $this->em->clear('Mautic\\LeadBundle\\Entity\\Lead');
             $this->em->clear('Mautic\\UserBundle\\Entity\\User');
             unset($leads, $campaignLeadIds, $leadLog);
             $currentCount = $max ? $totalEventCount : $negativeEvaluatedCount;
             if ($output && $currentCount < $maxCount) {
                 $progress->setProgress($currentCount);
             }
             // Free some memory
             gc_collect_cycles();
             ++$batchDebugCounter;
         }
         if ($output) {
             $progress->finish();
             $output->writeln('');
         }
     }
     $counts = ['events' => $nonActionEventCount, 'evaluated' => $negativeEvaluatedCount, 'executed' => $negativeExecutedCount, 'totalEvaluated' => $evaluatedEventCount, 'totalExecuted' => $executedEventCount];
     $this->logger->debug('CAMPAIGN: Counts - ' . var_export($counts, true));
     return $returnCounts ? $counts : $executedEventCount;
 }