![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/app/bundles/CampaignBundle/Command/ |
<?php namespace Mautic\CampaignBundle\Command; use Exception; use Mautic\CampaignBundle\CampaignEvents; use Mautic\CampaignBundle\Entity\Campaign; use Mautic\CampaignBundle\Entity\CampaignRepository; use Mautic\CampaignBundle\Event\CampaignTriggerEvent; use Mautic\CampaignBundle\Executioner\ContactFinder\Limiter\ContactLimiter; use Mautic\CampaignBundle\Executioner\InactiveExecutioner; use Mautic\CampaignBundle\Executioner\KickoffExecutioner; use Mautic\CampaignBundle\Executioner\ScheduledExecutioner; use Mautic\CoreBundle\Command\ModeratedCommand; use Mautic\CoreBundle\Helper\CoreParametersHelper; use Mautic\CoreBundle\Helper\PathsHelper; use Mautic\CoreBundle\Twig\Helper\FormatterHelper; use Mautic\LeadBundle\Helper\SegmentCountCacheHelper; use Mautic\LeadBundle\Model\ListModel; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\Translation\TranslatorInterface; class TriggerCampaignCommand extends ModeratedCommand { use WriteCountTrait; private bool $kickoffOnly = false; private bool $inactiveOnly = false; private bool $scheduleOnly = false; /** * @var OutputInterface */ protected $output; private ?ContactLimiter $limiter = null; private ?Campaign $campaign = null; public function __construct( private CampaignRepository $campaignRepository, private EventDispatcherInterface $dispatcher, private TranslatorInterface $translator, private KickoffExecutioner $kickoffExecutioner, private ScheduledExecutioner $scheduledExecutioner, private InactiveExecutioner $inactiveExecutioner, private LoggerInterface $logger, private FormatterHelper $formatterHelper, private ListModel $listModel, private SegmentCountCacheHelper $segmentCountCacheHelper, PathsHelper $pathsHelper, CoreParametersHelper $coreParametersHelper ) { parent::__construct($pathsHelper, $coreParametersHelper); } protected function configure() { $this ->setName('mautic:campaigns:trigger') ->addOption( '--campaign-id', '-i', InputOption::VALUE_OPTIONAL, 'Trigger events for a specific campaign. Otherwise, all campaigns will be triggered.', null ) ->addOption( '--campaign-limit', null, InputOption::VALUE_OPTIONAL, 'Limit number of contacts on a per campaign basis', null ) ->addOption( '--contact-id', null, InputOption::VALUE_OPTIONAL, 'Trigger events for a specific contact.', null ) ->addOption( '--contact-ids', null, InputOption::VALUE_OPTIONAL, 'CSV of contact IDs to evaluate.' ) ->addOption( '--min-contact-id', null, InputOption::VALUE_OPTIONAL, 'Trigger events starting at a specific contact ID.', null ) ->addOption( '--max-contact-id', null, InputOption::VALUE_OPTIONAL, 'Trigger events starting up to a specific contact ID.', null ) ->addOption( '--thread-id', null, InputOption::VALUE_OPTIONAL, 'The number of this current process if running multiple in parallel.' ) ->addOption( '--max-threads', null, InputOption::VALUE_OPTIONAL, 'The maximum number of processes you intend to run in parallel.' ) ->addOption( '--kickoff-only', null, InputOption::VALUE_NONE, 'Just kickoff the campaign' ) ->addOption( '--scheduled-only', null, InputOption::VALUE_NONE, 'Just execute scheduled events' ) ->addOption( '--inactive-only', null, InputOption::VALUE_NONE, 'Just execute scheduled events' ) ->addOption( '--batch-limit', '-l', InputOption::VALUE_OPTIONAL, 'Set batch size of contacts to process per round. Defaults to 100.', 100 ) ->addOption( 'exclude', 'd', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Exclude a specific campaign from being triggered. Otherwise, all campaigns will be triggered.', [] ); parent::configure(); } /** * @throws \Exception */ protected function execute(InputInterface $input, OutputInterface $output): int { $quiet = $input->getOption('quiet'); $this->output = $quiet ? new NullOutput() : $output; $this->kickoffOnly = $input->getOption('kickoff-only'); $this->scheduleOnly = $input->getOption('scheduled-only'); $this->inactiveOnly = $input->getOption('inactive-only'); $batchLimit = $input->getOption('batch-limit'); $campaignLimit = $input->getOption('campaign-limit'); $contactMinId = $input->getOption('min-contact-id'); $contactMaxId = $input->getOption('max-contact-id'); $contactId = $input->getOption('contact-id'); $contactIds = $this->formatterHelper->simpleCsvToArray($input->getOption('contact-ids'), 'int'); $threadId = $input->getOption('thread-id'); $maxThreads = $input->getOption('max-threads'); $excludeCampaigns = $input->getOption('exclude'); if ($threadId && $maxThreads && (int) $threadId > (int) $maxThreads) { $this->output->writeln('--thread-id cannot be larger than --max-thread'); return \Symfony\Component\Console\Command\Command::FAILURE; } $this->limiter = new ContactLimiter($batchLimit, $contactId, $contactMinId, $contactMaxId, $contactIds, $threadId, $maxThreads, $campaignLimit); defined('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED') or define('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED', 1); $id = $input->getOption('campaign-id'); $moderationKey = sprintf('%s-%s', $id, $threadId); if (!$this->checkRunStatus($input, $this->output, $moderationKey)) { return \Symfony\Component\Console\Command\Command::SUCCESS; } // Specific campaign; if ($id) { $statusCode = 0; /** @var Campaign $campaign */ if ($campaign = $this->campaignRepository->getEntity($id)) { $this->triggerCampaign($campaign); } else { $output->writeln('<error>'.$this->translator->trans('mautic.campaign.rebuild.not_found', ['%id%' => $id]).'</error>'); $statusCode = 1; } $this->completeRun(); return (int) $statusCode; } // All published campaigns $filter = [ 'iterable_mode' => true, 'orderBy' => 'c.dateAdded', 'orderByDir' => 'DESC', ]; // exclude excluded campaigns if (is_array($excludeCampaigns) && count($excludeCampaigns) > 0) { $filter['filter'] = [ 'force' => [ [ 'expr' => 'notIn', 'column' => $this->campaignRepository->getTableAlias().'.id', 'value' => $excludeCampaigns, ], ], ]; } /** @var \Doctrine\ORM\Internal\Hydration\IterableResult $campaigns */ $campaigns = $this->campaignRepository->getEntities($filter); foreach ($campaigns as $campaign) { $this->triggerCampaign($campaign); if ($this->limiter->hasCampaignLimit()) { $this->limiter->resetCampaignLimitRemaining(); } } $this->completeRun(); return \Symfony\Component\Console\Command\Command::SUCCESS; } /** * @return bool */ protected function dispatchTriggerEvent(Campaign $campaign) { if ($this->dispatcher->hasListeners(CampaignEvents::CAMPAIGN_ON_TRIGGER)) { /** @var CampaignTriggerEvent $event */ $event = $this->dispatcher->dispatch( new CampaignTriggerEvent($campaign), CampaignEvents::CAMPAIGN_ON_TRIGGER ); return $event->shouldTrigger(); } return true; } /** * @throws \Exception */ private function triggerCampaign(Campaign $campaign): void { if (!$campaign->isPublished()) { return; } if (!$this->dispatchTriggerEvent($campaign)) { return; } $this->campaign = $campaign; try { $this->output->writeln('<info>'.$this->translator->trans('mautic.campaign.trigger.triggering', ['%id%' => $campaign->getId()]).'</info>'); // Reset batch limiter $this->limiter->resetBatchMinContactId(); // Execute starting events if (!$this->inactiveOnly && !$this->scheduleOnly) { $this->executeKickoff(); } // Reset batch limiter $this->limiter->resetBatchMinContactId(); // Execute scheduled events if (!$this->inactiveOnly && !$this->kickoffOnly) { $this->executeScheduled(); } // Reset batch limiter $this->limiter->resetBatchMinContactId(); // Execute inactive events if (!$this->scheduleOnly && !$this->kickoffOnly) { $this->executeInactive(); } } catch (\Exception $exception) { if ('prod' !== MAUTIC_ENV) { // Throw the exception for dev/test mode throw $exception; } $this->logger->error('CAMPAIGN: '.$exception->getMessage()); } finally { // Update campaign linked segment cache count. $this->updateCampaignSegmentContactCount($campaign); } // Don't detach in tests since this command will be ran multiple times in the same process if ('test' !== MAUTIC_ENV) { $this->campaignRepository->detachEntity($campaign); } } /** * @throws \Mautic\CampaignBundle\Executioner\Dispatcher\Exception\LogNotProcessedException * @throws \Mautic\CampaignBundle\Executioner\Dispatcher\Exception\LogPassedAndFailedException * @throws \Mautic\CampaignBundle\Executioner\Exception\CannotProcessEventException * @throws \Mautic\CampaignBundle\Executioner\Scheduler\Exception\NotSchedulableException */ private function executeKickoff(): void { // trigger starting action events for newly added contacts $this->output->writeln('<comment>'.$this->translator->trans('mautic.campaign.trigger.starting').'</comment>'); $counter = $this->kickoffExecutioner->execute($this->campaign, $this->limiter, $this->output); $this->writeCounts($this->output, $this->translator, $counter); } /** * @throws \Doctrine\ORM\Query\QueryException * @throws \Mautic\CampaignBundle\Executioner\Dispatcher\Exception\LogNotProcessedException * @throws \Mautic\CampaignBundle\Executioner\Dispatcher\Exception\LogPassedAndFailedException * @throws \Mautic\CampaignBundle\Executioner\Exception\CannotProcessEventException * @throws \Mautic\CampaignBundle\Executioner\Scheduler\Exception\NotSchedulableException */ private function executeScheduled(): void { $this->output->writeln('<comment>'.$this->translator->trans('mautic.campaign.trigger.scheduled').'</comment>'); $counter = $this->scheduledExecutioner->execute($this->campaign, $this->limiter, $this->output); $this->writeCounts($this->output, $this->translator, $counter); } /** * @throws \Mautic\CampaignBundle\Executioner\Dispatcher\Exception\LogNotProcessedException * @throws \Mautic\CampaignBundle\Executioner\Dispatcher\Exception\LogPassedAndFailedException * @throws \Mautic\CampaignBundle\Executioner\Exception\CannotProcessEventException * @throws \Mautic\CampaignBundle\Executioner\Scheduler\Exception\NotSchedulableException */ private function executeInactive(): void { // find and trigger "no" path events $this->output->writeln('<comment>'.$this->translator->trans('mautic.campaign.trigger.negative').'</comment>'); $counter = $this->inactiveExecutioner->execute($this->campaign, $this->limiter, $this->output); $this->writeCounts($this->output, $this->translator, $counter); } /** * @throws \Exception */ private function updateCampaignSegmentContactCount(Campaign $campaign): void { $segmentIds = $this->campaignRepository->getCampaignListIds((int) $campaign->getId()); foreach ($segmentIds as $segmentId) { $totalLeadCount = $this->listModel->getRepository()->getLeadCount($segmentId); $this->segmentCountCacheHelper->setSegmentContactCount($segmentId, (int) $totalLeadCount); } } protected static $defaultDescription = 'Trigger timed events for published campaigns.'; }