Пример #1
0
 /**
  * {@inheritdoc}
  */
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $options = $input->getOptions();
     $force = $options['force'];
     $package = $options['update-package'];
     $appRoot = dirname($this->getContainer()->getParameter('kernel.root_dir'));
     /** @var \Symfony\Bundle\FrameworkBundle\Translation\Translator $translator */
     $translator = $this->getContainer()->get('translator');
     $translator->setLocale($this->getContainer()->get('mautic.factory')->getParameter('locale'));
     if ($package) {
         if (!file_exists($package)) {
             $output->writeln('<error>' . $translator->trans('mautic.core.update.archive_no_such_file') . '</error>');
             return 1;
         }
     }
     if (!$force) {
         /** @var \Symfony\Component\Console\Helper\SymfonyQuestionHelper $helper */
         $helper = $this->getHelperSet()->get('question');
         $question = new ConfirmationQuestion($translator->trans('mautic.core.update.confirm_application_update') . " ", false);
         if (!$helper->ask($input, $output, $question)) {
             $output->writeln($translator->trans('mautic.core.update.aborted'));
             return 0;
         }
     }
     // Start a progress bar, don't give a max number of steps because it is conditional
     $progressBar = ProgressBarHelper::init($output);
     $progressBar->setFormat('Step %current% [%bar%] <info>%message%</info>');
     if ($package) {
         $progressBar->setMessage($translator->trans('mautic.core.command.update.step.loading_package') . "                  ");
         $progressBar->advance();
         $zipFile = $package;
         $version = basename($package);
     } else {
         $progressBar->setMessage($translator->trans('mautic.core.command.update.step.loading_update_information') . "                  ");
         $progressBar->advance();
         $updateHelper = $this->getContainer()->get('mautic.helper.update');
         $update = $updateHelper->fetchData();
         if (!isset($update['package'])) {
             $output->writeln("\n\n<error>" . $translator->trans('mautic.core.update.no_cache_data') . '</error>');
             return 1;
         }
         $progressBar->setMessage($translator->trans('mautic.core.command.update.step.download_update_package') . "                  ");
         $progressBar->advance();
         // Fetch the update package
         $package = $updateHelper->fetchPackage($update['package']);
         if ($package['error']) {
             $output->writeln("\n\n<error>" . $translator->trans($package['message']) . '</error>');
             return 1;
         }
         $zipFile = $this->getContainer()->getParameter('kernel.cache_dir') . '/' . basename($update['package']);
         $version = $update['version'];
     }
     $progressBar->setMessage($translator->trans('mautic.core.command.update.step.validate_update_package') . "                  ");
     $progressBar->advance();
     $zipper = new \ZipArchive();
     $archive = $zipper->open($zipFile);
     if ($archive !== true) {
         // Get the exact error
         switch ($archive) {
             case \ZipArchive::ER_EXISTS:
                 $error = 'mautic.core.update.archive_file_exists';
                 break;
             case \ZipArchive::ER_INCONS:
             case \ZipArchive::ER_INVAL:
             case \ZipArchive::ER_MEMORY:
                 $error = 'mautic.core.update.archive_zip_corrupt';
                 break;
             case \ZipArchive::ER_NOENT:
                 $error = 'mautic.core.update.archive_no_such_file';
                 break;
             case \ZipArchive::ER_NOZIP:
                 $error = 'mautic.core.update.archive_not_valid_zip';
                 break;
             case \ZipArchive::ER_READ:
             case \ZipArchive::ER_SEEK:
             case \ZipArchive::ER_OPEN:
             default:
                 $error = 'mautic.core.update.archive_could_not_open';
                 break;
         }
         $output->writeln("\n\n<error>" . $translator->trans('mautic.core.update.error', ['%error%' => $translator->trans($error)]) . '</error>');
         return 1;
     }
     // Extract the archive file now in place
     $progressBar->setMessage($translator->trans('mautic.core.update.step.extracting.package') . "                  ");
     $progressBar->advance();
     if (!$zipper->extractTo($appRoot)) {
         $output->writeln("\n\n<error>" . $translator->trans('mautic.core.update.error', ['%error%' => $translator->trans('mautic.core.update.error_extracting_package')]) . '</error>');
         return 1;
     }
     $zipper->close();
     // Define this just in case
     defined('MAUTIC_ENV') or define('MAUTIC_ENV', isset($options['env']) ? $options['env'] : 'prod');
     // Make sure we have a deleted_files list otherwise we can't process this step
     if (file_exists($appRoot . '/deleted_files.txt')) {
         $progressBar->setMessage($translator->trans('mautic.core.update.remove.deleted.files') . "                  ");
         $progressBar->advance();
         $deletedFiles = json_decode(file_get_contents($appRoot . '/deleted_files.txt'), true);
         $errorLog = [];
         // Before looping over the deleted files, add in our upgrade specific files
         $deletedFiles += ['deleted_files.txt', 'upgrade.php'];
         foreach ($deletedFiles as $file) {
             $path = $appRoot . '/' . $file;
             if (file_exists($path)) {
                 // Try setting the permissions to 777 just to make sure we can get rid of the file
                 @chmod($path, 0777);
                 if (!@unlink($path)) {
                     // Failed to delete, reset the permissions to 644 for safety
                     @chmod($path, 0644);
                     $errorLog[] = $translator->trans('mautic.core.update.error.removing.file', ['%path%' => $file]);
                 }
             }
         }
         // If there were any errors, add them to the error log
         if (count($errorLog)) {
             // Check if the error log exists first
             if (file_exists($appRoot . '/upgrade_errors.txt')) {
                 $errors = file_get_contents($appRoot . '/upgrade_errors.txt');
             } else {
                 $errors = '';
             }
             $errors .= implode(PHP_EOL, $errorLog);
             @file_put_contents($appRoot . '/upgrade_errors.txt', $errors);
         }
     }
     // Clear the dev and prod cache instances to reset the system
     $progressBar->setMessage($translator->trans('mautic.core.update.clear.cache') . "                  ");
     $progressBar->advance();
     $cacheHelper = $this->getContainer()->get('mautic.helper.cache');
     $cacheHelper->nukeCache();
     // Update languages
     $supportedLanguages = $this->getContainer()->get('mautic.factory')->getParameter('supported_languages');
     // If there is only one language, assume it is 'en_US' and skip this
     if (count($supportedLanguages) > 1) {
         $progressBar->setMessage($translator->trans('mautic.core.command.update.step.update_languages' . "                  "));
         $progressBar->advance();
         /** @var \Mautic\CoreBundle\Helper\LanguageHelper $languageHelper */
         $languageHelper = $this->getContainer()->get('mautic.factory')->getHelper('language');
         // First, update the cached language data
         $result = $languageHelper->fetchLanguages(true);
         // Only continue if not in error
         if (!isset($result['error'])) {
             foreach ($supportedLanguages as $locale => $name) {
                 // We don't need to update en_US, that comes with the main package
                 if ($locale == 'en_US') {
                     continue;
                 }
                 // Update time
                 $extractResult = $languageHelper->extractLanguagePackage($locale);
                 if ($extractResult['error']) {
                     $output->writeln("\n\n<error>" . $translator->trans('mautic.core.update.error_updating_language', ['%language%' => $name]) . '</error>');
                 }
             }
         }
     }
     // Migrate the database to the current version
     $progressBar->setMessage($translator->trans('mautic.core.update.migrating.database.schema' . "                  "));
     $progressBar->advance();
     $migrationApplication = new Application($this->getContainer()->get('kernel'));
     $migrationApplication->setAutoExit(false);
     $migrationCommandArgs = new ArgvInput(['console', 'doctrine:migrations:migrate', '--quiet', '--no-interaction']);
     $migrationCommandArgs->setInteractive(false);
     $migrateExitCode = $migrationApplication->run($migrationCommandArgs, new NullOutput());
     unset($migrationApplication);
     $progressBar->setMessage($translator->trans('mautic.core.update.step.wrapping_up' . "                  "));
     $progressBar->advance();
     // Clear the cached update data and the download package now that we've updated
     if (empty($package)) {
         @unlink($zipFile);
     } else {
         @unlink($this->getContainer()->getParameter('kernel.cache_dir') . '/lastUpdateCheck.txt');
     }
     @unlink($appRoot . '/deleted_files.txt');
     @unlink($appRoot . '/upgrade.php');
     // Update successful
     $progressBar->setMessage($translator->trans('mautic.core.update.update_successful', ['%version%' => $version]) . "                  ");
     $progressBar->finish();
     // Check for a post install message
     if ($postMessage = $this->getContainer()->get('session')->get('post_upgrade_message', false)) {
         $postMessage = strip_tags($postMessage);
         $this->getContainer()->get('session')->remove('post_upgrade_message');
         $output->writeln("\n\n<info>{$postMessage}</info>");
     }
     // Output the error (if exists) from the migrate command after we've finished the progress bar
     if ($migrateExitCode !== 0) {
         $output->writeln("\n\n<error>" . $translator->trans('mautic.core.update.error_performing_migration') . '</error>');
     }
     return 0;
 }
Пример #2
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;
 }
Пример #3
0
 /**
  * Rebuild lead lists
  *
  * @param LeadList        $entity
  * @param int             $limit
  * @param bool            $maxLeads
  * @param OutputInterface $output
  *
  * @return int
  */
 public function rebuildListLeads(LeadList $entity, $limit = 1000, $maxLeads = false, OutputInterface $output = null)
 {
     defined('MAUTIC_REBUILDING_LEAD_LISTS') or define('MAUTIC_REBUILDING_LEAD_LISTS', 1);
     $id = $entity->getId();
     $list = array('id' => $id, 'filters' => $entity->getFilters());
     $dtHelper = new DateTimeHelper();
     $batchLimiters = array('dateTime' => $dtHelper->toUtcString());
     $localDateTime = $dtHelper->getLocalDateTime();
     // Get a count of leads to add
     $newLeadsCount = $this->getLeadsByList($list, true, array('countOnly' => true, 'newOnly' => true, 'batchLimiters' => $batchLimiters));
     // Ensure the same list is used each batch
     $batchLimiters['maxId'] = (int) $newLeadsCount[$id]['maxId'];
     // Number of total leads to process
     $leadCount = (int) $newLeadsCount[$id]['count'];
     if ($output) {
         $output->writeln($this->translator->trans('mautic.lead.list.rebuild.to_be_added', array('%leads%' => $leadCount, '%batch%' => $limit)));
     }
     // Handle by batches
     $start = $lastRoundPercentage = $leadsProcessed = 0;
     // Try to save some memory
     gc_enable();
     if ($leadCount) {
         $maxCount = $maxLeads ? $maxLeads : $leadCount;
         if ($output) {
             $progress = ProgressBarHelper::init($output, $maxCount);
             $progress->start();
         }
         // Add leads
         while ($start < $leadCount) {
             // Keep CPU down for large lists; sleep per $limit batch
             $this->batchSleep();
             $newLeadList = $this->getLeadsByList($list, true, array('newOnly' => true, 'limit' => $limit, 'batchLimiters' => $batchLimiters));
             if (empty($newLeadList[$id])) {
                 // Somehow ran out of leads so break out
                 break;
             }
             foreach ($newLeadList[$id] as $l) {
                 $this->addLead($l, $entity, false, true, -1, $localDateTime);
                 unset($l);
                 $leadsProcessed++;
                 if ($output && $leadsProcessed < $maxCount) {
                     $progress->setProgress($leadsProcessed);
                 }
                 if ($maxLeads && $leadsProcessed >= $maxLeads) {
                     break;
                 }
             }
             $start += $limit;
             // Dispatch batch event
             if ($this->dispatcher->hasListeners(LeadEvents::LEAD_LIST_BATCH_CHANGE)) {
                 $event = new ListChangeEvent($newLeadList[$id], $entity, true);
                 $this->dispatcher->dispatch(LeadEvents::LEAD_LIST_BATCH_CHANGE, $event);
                 unset($event);
             }
             unset($newLeadList);
             // Free some memory
             gc_collect_cycles();
             if ($maxLeads && $leadsProcessed >= $maxLeads) {
                 if ($output) {
                     $progress->finish();
                     $output->writeln('');
                 }
                 return $leadsProcessed;
             }
         }
         if ($output) {
             $progress->finish();
             $output->writeln('');
         }
     }
     // Get a count of leads to be removed
     $removeLeadCount = $this->getLeadsByList($list, true, array('countOnly' => true, 'nonMembersOnly' => true, 'batchLimiters' => $batchLimiters));
     // Restart batching
     $start = $lastRoundPercentage = 0;
     $leadCount = $removeLeadCount[$id]['count'];
     if ($output) {
         $output->writeln($this->translator->trans('mautic.lead.list.rebuild.to_be_removed', array('%leads%' => $leadCount, '%batch%' => $limit)));
     }
     if ($leadCount) {
         $maxCount = $maxLeads ? $maxLeads : $leadCount;
         if ($output) {
             $progress = ProgressBarHelper::init($output, $maxCount);
             $progress->start();
         }
         // Remove leads
         while ($start < $leadCount) {
             // Keep CPU down for large lists; sleep per $limit batch
             $this->batchSleep();
             $removeLeadList = $this->getLeadsByList($list, true, array('limit' => $limit, 'nonMembersOnly' => true, 'batchLimiters' => $batchLimiters));
             if (empty($removeLeadList[$id])) {
                 // Somehow ran out of leads so break out
                 break;
             }
             foreach ($removeLeadList[$id] as $l) {
                 $this->removeLead($l, $entity, false, true, true);
                 $leadsProcessed++;
                 if ($output && $leadsProcessed < $maxCount) {
                     $progress->setProgress($leadsProcessed);
                 }
                 if ($maxLeads && $leadsProcessed >= $maxLeads) {
                     break;
                 }
             }
             // Dispatch batch event
             if ($this->dispatcher->hasListeners(LeadEvents::LEAD_LIST_BATCH_CHANGE)) {
                 $event = new ListChangeEvent($removeLeadList[$id], $entity, false);
                 $this->dispatcher->dispatch(LeadEvents::LEAD_LIST_BATCH_CHANGE, $event);
                 unset($event);
             }
             $start += $limit;
             unset($removeLeadList);
             // Free some memory
             gc_collect_cycles();
             if ($maxLeads && $leadsProcessed >= $maxLeads) {
                 if ($output) {
                     $progress->finish();
                     $output->writeln('');
                 }
                 return $leadsProcessed;
             }
         }
         if ($output) {
             $progress->finish();
             $output->writeln('');
         }
     }
     return $leadsProcessed;
 }
Пример #4
0
 /**
  * @param Campaign        $campaign
  * @param int             $limit
  * @param bool            $maxLeads
  * @param OutputInterface $output
  *
  * @return int
  */
 public function rebuildCampaignLeads(Campaign $campaign, $limit = 1000, $maxLeads = false, OutputInterface $output = null)
 {
     defined('MAUTIC_REBUILDING_CAMPAIGNS') or define('MAUTIC_REBUILDING_CAMPAIGNS', 1);
     $repo = $this->getRepository();
     // Get a list of lead lists this campaign is associated with
     $lists = $repo->getCampaignListIds($campaign->getId());
     $batchLimiters = array('dateTime' => (new DateTimeHelper())->toUtcString());
     if (count($lists)) {
         // Get a count of new leads
         $newLeadsCount = $repo->getCampaignLeadsFromLists($campaign->getId(), $lists, array('countOnly' => true, 'batchLimiters' => $batchLimiters));
         // Ensure the same list is used each batch
         $batchLimiters['maxId'] = (int) $newLeadsCount['maxId'];
         // Number of total leads to process
         $leadCount = (int) $newLeadsCount['count'];
     } else {
         // No lists to base campaign membership off of so ignore
         $leadCount = 0;
     }
     if ($output) {
         $output->writeln($this->translator->trans('mautic.campaign.rebuild.to_be_added', array('%leads%' => $leadCount, '%batch%' => $limit)));
     }
     // Handle by batches
     $start = $leadsProcessed = 0;
     // Try to save some memory
     gc_enable();
     if ($leadCount) {
         $maxCount = $maxLeads ? $maxLeads : $leadCount;
         if ($output) {
             $progress = ProgressBarHelper::init($output, $maxCount);
             $progress->start();
         }
         // Add leads
         while ($start < $leadCount) {
             // Keep CPU down for large lists; sleep per $limit batch
             $this->batchSleep();
             // Get a count of new leads
             $newLeadList = $repo->getCampaignLeadsFromLists($campaign->getId(), $lists, array('limit' => $limit, 'batchLimiters' => $batchLimiters));
             $start += $limit;
             foreach ($newLeadList as $l) {
                 $this->addLeads($campaign, array($l), false, true, -1);
                 $leadsProcessed++;
                 if ($output && $leadsProcessed < $maxCount) {
                     $progress->setProgress($leadsProcessed);
                 }
                 unset($l);
                 if ($maxLeads && $leadsProcessed >= $maxLeads) {
                     // done for this round, bye bye
                     if ($output) {
                         $progress->finish();
                     }
                     return $leadsProcessed;
                 }
             }
             unset($newLeadList);
             // Free some memory
             gc_collect_cycles();
         }
         if ($output) {
             $progress->finish();
             $output->writeln('');
         }
     }
     // Get a count of leads to be removed
     $removeLeadCount = $repo->getCampaignOrphanLeads($campaign->getId(), $lists, array('countOnly' => true, 'batchLimiters' => $batchLimiters));
     // Restart batching
     $start = $lastRoundPercentage = 0;
     $leadCount = $removeLeadCount['count'];
     $batchLimiters['maxId'] = $removeLeadCount['maxId'];
     if ($output) {
         $output->writeln($this->translator->trans('mautic.lead.list.rebuild.to_be_removed', array('%leads%' => $leadCount, '%batch%' => $limit)));
     }
     if ($leadCount) {
         $maxCount = $maxLeads ? $maxLeads : $leadCount;
         if ($output) {
             $progress = ProgressBarHelper::init($output, $maxCount);
             $progress->start();
         }
         // Remove leads
         while ($start < $leadCount) {
             // Keep CPU down for large lists; sleep per $limit batch
             $this->batchSleep();
             $removeLeadList = $repo->getCampaignOrphanLeads($campaign->getId(), $lists, array('limit' => $limit, 'batchLimiters' => $batchLimiters));
             foreach ($removeLeadList as $l) {
                 $this->removeLeads($campaign, array($l), false, true, true);
                 $leadsProcessed++;
                 if ($output && $leadsProcessed < $maxCount) {
                     $progress->setProgress($leadsProcessed);
                 }
                 if ($maxLeads && $leadsProcessed >= $maxLeads) {
                     // done for this round, bye bye
                     $progress->finish();
                     return $leadsProcessed;
                 }
             }
             $start += $limit;
             unset($removeLeadList);
             // Free some memory
             gc_collect_cycles();
         }
         if ($output) {
             $progress->finish();
             $output->writeln('');
         }
     }
     return $leadsProcessed;
 }