![]() 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/PointBundle/Model/ |
<?php namespace Mautic\PointBundle\Model; use Doctrine\ORM\EntityManagerInterface; use Mautic\CoreBundle\Factory\MauticFactory; use Mautic\CoreBundle\Helper\CoreParametersHelper; use Mautic\CoreBundle\Helper\DateTimeHelper; use Mautic\CoreBundle\Helper\IpLookupHelper; use Mautic\CoreBundle\Helper\UserHelper; use Mautic\CoreBundle\Model\FormModel as CommonFormModel; use Mautic\CoreBundle\Security\Permissions\CorePermissions; use Mautic\CoreBundle\Translation\Translator; use Mautic\LeadBundle\Entity\Lead; use Mautic\LeadBundle\Entity\LeadRepository; use Mautic\LeadBundle\Model\LeadModel; use Mautic\LeadBundle\Tracker\ContactTracker; use Mautic\PointBundle\Entity\GroupContactScore; use Mautic\PointBundle\Entity\LeadTriggerLog; use Mautic\PointBundle\Entity\Trigger; use Mautic\PointBundle\Entity\TriggerEvent; use Mautic\PointBundle\Event as Events; use Mautic\PointBundle\Form\Type\TriggerType; use Mautic\PointBundle\PointEvents; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\EventDispatcher\Event; /** * @extends CommonFormModel<Trigger> */ class TriggerModel extends CommonFormModel { protected $triggers = []; /** * @var array<string,array<string,mixed>> */ private static array $events; public function __construct( protected IpLookupHelper $ipLookupHelper, protected LeadModel $leadModel, protected TriggerEventModel $pointTriggerEventModel, /** * @deprecated https://github.com/mautic/mautic/issues/8229 */ protected MauticFactory $mauticFactory, private ContactTracker $contactTracker, EntityManagerInterface $em, CorePermissions $security, EventDispatcherInterface $dispatcher, UrlGeneratorInterface $router, Translator $translator, UserHelper $userHelper, LoggerInterface $mauticLogger, CoreParametersHelper $coreParametersHelper ) { parent::__construct($em, $security, $dispatcher, $router, $translator, $userHelper, $mauticLogger, $coreParametersHelper); } /** * @return \Mautic\PointBundle\Entity\TriggerRepository */ public function getRepository() { return $this->em->getRepository(Trigger::class); } /** * Retrieves an instance of the TriggerEventRepository. * * @return \Mautic\PointBundle\Entity\TriggerEventRepository */ public function getEventRepository() { return $this->em->getRepository(TriggerEvent::class); } public function getPermissionBase(): string { return 'point:triggers'; } /** * @throws MethodNotAllowedHttpException */ public function createForm($entity, FormFactoryInterface $formFactory, $action = null, $options = []): \Symfony\Component\Form\FormInterface { if (!$entity instanceof Trigger) { throw new MethodNotAllowedHttpException(['Trigger']); } if (!empty($action)) { $options['action'] = $action; } return $formFactory->create(TriggerType::class, $entity, $options); } /** * @param Trigger $entity * @param bool $unlock */ public function saveEntity($entity, $unlock = true): void { $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 = []; $ipAddress = $this->ipLookupHelper->getIpAddress(); $pointGroup = $entity->getGroup(); /** @var LeadRepository $leadRepository */ $leadRepository = $this->em->getRepository(Lead::class); foreach ($events as $event) { $args = [ 'filter' => [ 'force' => [ [ 'column' => 'l.date_added', 'expr' => 'lte', 'value' => (new DateTimeHelper($entity->getDateAdded()))->toUtcString(), ], ], ], ]; if (!$pointGroup) { $args['filter']['force'][] = [ 'column' => 'l.points', 'expr' => 'gte', 'value' => $entity->getPoints(), ]; } else { $args['qb'] = $leadRepository->getEntitiesDbalQueryBuilder() ->leftJoin('l', MAUTIC_TABLE_PREFIX.GroupContactScore::TABLE_NAME, 'pls', 'l.id = pls.contact_id'); $args['filter']['force'][] = [ 'column' => 'pls.score', 'expr' => 'gte', 'value' => $entity->getPoints(), ]; $args['filter']['force'][] = [ 'column' => 'pls.group_id', 'expr' => 'eq', 'value' => $entity->getGroup()->getId(), ]; } if (!$isNew) { // get a list of leads that has already had this event applied $leadIds = $repo->getLeadsForEvent($event->getId()); if (!empty($leadIds)) { $args['filter']['force'][] = [ '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($args); /** @var Lead $l */ 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); } } } public function getEntity($id = null): ?Trigger { if (null === $id) { return new Trigger(); } return parent::getEntity($id); } /** * @throws MethodNotAllowedHttpException */ protected function dispatchEvent($action, &$entity, $isNew = false, Event $event = null): ?Event { if (!$entity instanceof Trigger) { throw new MethodNotAllowedHttpException(['Trigger']); } switch ($action) { case 'pre_save': $name = PointEvents::TRIGGER_PRE_SAVE; break; case 'post_save': $name = PointEvents::TRIGGER_POST_SAVE; break; case 'pre_delete': $name = PointEvents::TRIGGER_PRE_DELETE; break; case 'post_delete': $name = PointEvents::TRIGGER_POST_DELETE; break; default: return null; } if ($this->dispatcher->hasListeners($name)) { if (empty($event)) { $event = new Events\TriggerEvent($entity, $isNew); } $this->dispatcher->dispatch($event, $name); return $event; } return null; } /** * @param array $sessionEvents */ public function setEvents(Trigger $entity, $sessionEvents): void { $order = 1; $existingActions = $entity->getEvents(); foreach ($sessionEvents as $properties) { $isNew = (!empty($properties['id']) && isset($existingActions[$properties['id']])) ? false : true; $event = !$isNew ? $existingActions[$properties['id']] : new TriggerEvent(); foreach ($properties as $f => $v) { if (in_array($f, ['id', 'order'])) { continue; } $func = 'set'.ucfirst($f); if (method_exists($event, $func)) { $event->$func($v); } } $event->setTrigger($entity); $event->setOrder($order); ++$order; $entity->addTriggerEvent($properties['id'], $event); } // Persist if editing the trigger if ($entity->getId()) { $this->pointTriggerEventModel->saveEntities($entity->getEvents()); } } /** * Gets array of custom events from bundles subscribed PointEvents::TRIGGER_ON_BUILD. * * @return mixed[] */ public function getEvents() { if (empty(self::$events)) { // build them self::$events = []; $event = new Events\TriggerBuilderEvent($this->translator); $this->dispatcher->dispatch($event, PointEvents::TRIGGER_ON_BUILD); self::$events = $event->getEvents(); } return self::$events; } /** * Gets array of custom events from bundles inside groups. * * @return mixed[] */ public function getEventGroups(): array { $events = $this->getEvents(); $groups = []; foreach ($events as $key => $event) { $groups[$event['group']][$key] = $event; } return $groups; } /** * Triggers a specific event. * * @param array $event triggerEvent converted to array * @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 (null === $lead) { $lead = $this->contactTracker->getContact(); } 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]; if (isset($settings['callback']) && is_callable($settings['callback'])) { return $this->invokeCallback($event, $lead, $settings); } else { /** @var TriggerEvent $triggerEvent */ $triggerEvent = $this->getEventRepository()->find($event['id']); $triggerExecutedEvent = new Events\TriggerExecutedEvent($triggerEvent, $lead); $this->dispatcher->dispatch($triggerExecutedEvent, $settings['eventName']); return $triggerExecutedEvent->getResult(); } } /** * @return bool */ private function invokeCallback($event, Lead $lead, array $settings) { $args = [ 'event' => $event, 'lead' => $lead, 'factory' => $this->mauticFactory, 'config' => $event['properties'], ]; if (is_array($settings['callback'])) { $reflection = new \ReflectionMethod($settings['callback'][0], $settings['callback'][1]); } elseif (str_contains($settings['callback'], '::')) { $parts = explode('::', $settings['callback']); $reflection = new \ReflectionMethod($parts[0], $parts[1]); } else { $reflection = new \ReflectionMethod(null, $settings['callback']); } $pass = []; foreach ($reflection->getParameters() as $param) { if (isset($args[$param->getName()])) { $pass[] = $args[$param->getName()]; } else { $pass[] = null; } } return $reflection->invokeArgs($this, $pass); } /** * Trigger events for the current lead. */ public function triggerEvents(Lead $lead): void { $points = $lead->getPoints(); // find all published triggers that is applicable to this points /** @var \Mautic\PointBundle\Entity\TriggerEventRepository $repo */ $repo = $this->getEventRepository(); $events = $repo->getPublishedByPointTotal($points); $groupEvents = $repo->getPublishedByGroupScore($lead->getGroupScores()); $events = array_merge($events, $groupEvents); if (!empty($events)) { // get a list of actions that has already been applied to this lead $appliedEvents = $repo->getLeadTriggeredEvents($lead->getId()); $ipAddress = $this->ipLookupHelper->getIpAddress(); $persist = []; foreach ($events as $event) { if (isset($appliedEvents[$event['id']])) { // don't apply the event to the lead if it's already been done continue; } if ($this->triggerEvent($event, $lead, true)) { $log = new LeadTriggerLog(); $log->setIpAddress($ipAddress); $log->setEvent($triggerEvent = $this->getEventRepository()->find($event['id'])); $log->setLead($lead); $log->setDateFired(new \DateTime()); $persist[] = $log; } } if (!empty($persist)) { $this->getEventRepository()->saveEntities($persist); $this->getEventRepository()->detachEntities($persist); if (isset($triggerEvent)) { $this->getEventRepository()->deleteEntity($triggerEvent); } } } } /** * Returns configured color based on passed in $points. * * @return string */ public function getColorForLeadPoints($points) { if (!$this->triggers) { $this->triggers = $this->getRepository()->getTriggerColors(); } foreach ($this->triggers as $trigger) { if ($points >= $trigger['points']) { return $trigger['color']; } } return ''; } }