/** * @param $tokens * @param $config * @param MauticFactory $factory * @param Lead $lead */ public static function sendEmail($tokens, $config, MauticFactory $factory, Lead $lead) { $mailer = $factory->getMailer(); $emails = !empty($config['to']) ? explode(',', $config['to']) : array(); $mailer->setTo($emails); $leadEmail = $lead->getEmail(); if (!empty($leadEmail)) { // Reply to lead for user convenience $mailer->setReplyTo($leadEmail); } if (!empty($config['cc'])) { $emails = explode(',', $config['cc']); $mailer->setCc($emails); } if (!empty($config['bcc'])) { $emails = explode(',', $config['bcc']); $mailer->setBcc($emails); } $mailer->setSubject($config['subject']); $mailer->setTokens($tokens); $mailer->setBody($config['message']); $mailer->parsePlainText($config['message']); $mailer->send(); if ($config['copy_lead'] && !empty($leadEmail)) { // Send copy to lead $mailer->reset(); $mailer->setTo($leadEmail); $mailer->setSubject($config['subject']); $mailer->setTokens($tokens); $mailer->setBody($config['message']); $mailer->parsePlainText($config['message']); $mailer->send(); } }
/** * @param array $parameters * @param Lead $lead * @param CompanyModel $companyModel * * @return array */ public static function identifyLeadsCompany($parameters, Lead $lead, CompanyModel $companyModel) { $companyName = $companyDomain = null; $leadAdded = false; if (isset($parameters['company'])) { $companyName = filter_var($parameters['company'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); } elseif (isset($parameters['email'])) { $companyName = $companyDomain = self::domainExists($parameters['email']); } if ($companyName) { $companyRepo = $companyModel->getRepository(); $city = isset($parameters['city']) ? $parameters['city'] : null; $country = isset($parameters['country']) ? $parameters['country'] : null; $state = isset($parameters['state']) ? $parameters['state'] : null; $company = $companyRepo->identifyCompany($companyName, $city, $country, $state); if (!empty($company)) { //check if lead is already assigned to company $companyLeadRepo = $companyModel->getCompanyLeadRepository(); if (empty($companyLeadRepo->getCompaniesByLeadId($lead->getId(), $company['id']))) { $leadAdded = true; } } else { //create new company $company = ['companyname' => $companyName, 'companywebsite' => $companyDomain, 'companycity' => $city, 'companystate' => $state, 'companycountry' => $country]; $companyEntity = $companyModel->getEntity(); $companyModel->setFieldValues($companyEntity, $company, true); $companyModel->saveEntity($companyEntity); $company['id'] = $companyEntity->getId(); $leadAdded = true; } return [$company, $leadAdded]; } return [null, false]; }
/** * @return array */ public function getClickthrough() { if (!in_array('lead', $this->clickthrough)) { $this->clickthrough['lead'] = is_array($this->lead) ? $this->lead['id'] : $this->lead->getId(); } return $this->clickthrough; }
/** * @param Lead $lead * @param array $options * * @return array */ public function getLeadIpLogs(Lead $lead, array $options = []) { $qb = $this->getEntityManager()->getConnection()->createQueryBuilder(); $sqb = $this->getEntityManager()->getConnection()->createQueryBuilder(); // Just a check to ensure reused IDs (happens with innodb) doesn't infect data $dt = new DateTimeHelper($lead->getDateAdded(), 'Y-m-d H:i:s', 'local'); $sqb->select('MAX(l.date_added) as date_added, l.ip_address')->from(MAUTIC_TABLE_PREFIX . 'audit_log', 'l')->where($sqb->expr()->andX($sqb->expr()->eq('l.bundle', $sqb->expr()->literal('lead')), $sqb->expr()->eq('l.object', $sqb->expr()->literal('lead')), $sqb->expr()->eq('l.action', $sqb->expr()->literal('ipadded')), $sqb->expr()->eq('l.object_id', $lead->getId()), $sqb->expr()->gte('l.date_added', $sqb->expr()->literal($dt->getUtcTimestamp()))))->groupBy('l.ip_address'); $qb->select('ip.date_added, ip.ip_address')->from(sprintf('(%s)', $sqb->getSQL()), 'ip'); return $this->getTimelineResults($qb, $options, 'ip.ip_address', 'ip.date_added', [], ['date_added']); }
/** * Get tag entities by lead. * * @param $utmTags * * @return array */ public function getUtmTagsByLead(Lead $lead, $options = []) { if (empty($lead)) { return []; } $qb = $this->getEntityManager()->getConnection()->createQueryBuilder()->select('*')->from(MAUTIC_TABLE_PREFIX . 'lead_utmtags', 'ut')->where('ut.lead_id = ' . $lead->getId()); if (isset($options['search']) && $options['search']) { $qb->andWhere($qb->expr()->orX($qb->expr()->like('ut.utm_campaign', $qb->expr()->literal('%' . $options['search'] . '%')), $qb->expr()->like('ut.utm_content', $qb->expr()->literal('%' . $options['search'] . '%')), $qb->expr()->like('ut.utm_medium', $qb->expr()->literal('%' . $options['search'] . '%')), $qb->expr()->like('ut.utm_source', $qb->expr()->literal('%' . $options['search'] . '%')), $qb->expr()->like('ut.utm_term', $qb->expr()->literal('%' . $options['search'] . '%')))); } return $this->getTimelineResults($qb, $options, 'ut.utm_campaign', 'ut.date_added', ['query'], ['date_added']); }
/** * @param $event * @param Lead $lead * * @return bool */ public static function validatePointChange($event, Lead $lead) { $properties = $event['properties']; $checkPoints = $properties['points']; if (!empty($checkPoints)) { $points = $lead->getPoints(); if ($points < $checkPoints) { return false; } } return true; }
/** * @param Lead $lead * @param array $action * * @return int */ public static function engagePointAction($lead, $action) { static $initiated = []; $pointsChange = 0; //only initiate once per lead per type if (empty($initiated[$lead->getId()][$action['type']])) { if (!empty($action['properties']['delta'])) { $pointsChange = $action['properties']['delta']; } } return $pointsChange; }
/** * Determine if this campaign applies * * @param $eventDetails * @param $event * * @return bool */ public static function validateFormValue(MauticFactory $factory, $event, Lead $lead) { if (!$lead || !$lead->getId()) { return false; } $model = $factory->getModel('form'); $operators = $model->getFilterExpressionFunctions(); $form = $model->getRepository()->findOneById($event['properties']['form']); if (!$form || !$form->getId()) { return false; } return $factory->getModel('form.submission')->getRepository()->compareValue($lead->getId(), $form->getId(), $form->getAlias(), $event['properties']['field'], $event['properties']['value'], $operators[$event['properties']['operator']]['expr']); }
/** * @param Lead $lead * @param array|null $filters * @param array|null $orderBy * @param int $page * * @return array */ protected function getEngagements(Lead $lead, array $filters = null, array $orderBy = null, $page = 1) { $session = $this->get('session'); if (null == $filters) { $filters = $session->get('mautic.lead.' . $lead->getId() . '.timeline.filters', ['search' => '', 'includeEvents' => [], 'excludeEvents' => []]); } if (null == $orderBy) { if (!$session->has('mautic.lead.' . $lead->getId() . '.timeline.orderby')) { $session->set('mautic.lead.' . $lead->getId() . '.timeline.orderby', 'timestamp'); $session->set('mautic.lead.' . $lead->getId() . '.timeline.orderbydir', 'DESC'); } $orderBy = [$session->get('mautic.lead.' . $lead->getId() . '.timeline.orderby'), $session->get('mautic.lead.' . $lead->getId() . '.timeline.orderbydir')]; } /** @var LeadModel $model */ $model = $this->getModel('lead'); return $model->getEngagements($lead, $filters, $orderBy, $page); }
/** * @param $event * @param Lead $lead * @param MauticFactory $factory */ public static function sendEmail($event, Lead $lead, MauticFactory $factory) { $properties = $event['properties']; $emailId = (int) $properties['email']; /** @var \Mautic\EmailBundle\Model\EmailModel $model */ $model = $factory->getModel('email'); $email = $model->getEntity($emailId); //make sure the email still exists and is published if ($email != null && $email->isPublished()) { $leadFields = $lead->getFields(); if (isset($leadFields['core']['email']['value']) && $leadFields['core']['email']['value']) { $leadCredentials = array('email' => $leadFields['core']['email']['value'], 'id' => $lead->getId(), 'firstname' => $leadFields['core']['firstname']['value'], 'lastname' => $leadFields['core']['lastname']['value']); $options = array('source' => array('trigger', $event['id'])); $model->sendEmail($email, $leadCredentials, $options); } } }
/** * @param $tokens * @param $config * @param MauticFactory $factory * @param Lead $lead */ public static function sendEmail($tokens, $config, MauticFactory $factory, Lead $lead) { // replace line brakes with <br> for textarea values if ($tokens) { foreach ($tokens as $token => &$value) { $value = nl2br(html_entity_decode($value)); } } $leadEmail = $lead->getEmail(); $mailer = $factory->getMailer(); $emails = !empty($config['to']) ? array_fill_keys(explode(',', $config['to']), null) : []; if (!empty($emails)) { $mailer->setTo($emails); if (!empty($leadEmail)) { // Reply to lead for user convenience $mailer->setReplyTo($leadEmail); } if (!empty($config['cc'])) { $emails = array_fill_keys(explode(',', $config['cc']), null); $mailer->setCc($emails); } if (!empty($config['bcc'])) { $emails = array_fill_keys(explode(',', $config['bcc']), null); $mailer->setBcc($emails); } $mailer->setSubject($config['subject']); $mailer->addTokens($tokens); $mailer->setBody($config['message']); $mailer->parsePlainText($config['message']); $mailer->send(true); } if ($config['copy_lead'] && !empty($leadEmail)) { // Send copy to lead $mailer->reset(); $mailer->setLead($lead->getProfileFields()); $mailer->setTo($leadEmail); $mailer->setSubject($config['subject']); $mailer->addTokens($tokens); $mailer->setBody($config['message']); $mailer->parsePlainText($config['message']); $mailer->send(true); } }
public function applyFrequencyRules(Lead $lead) { $frequencyRule = $lead->getFrequencyRules(); $statRepo = $this->smsModel->getStatRepository(); $now = new \DateTime(); $channels = $frequencyRule['channels']; $frequencyTime = $frequencyNumber = null; if (!empty($frequencyRule) && in_array('sms', $channels, true)) { $frequencyTime = new \DateInterval('P' . $frequencyRule['frequency_time']); $frequencyNumber = $frequencyRule['frequency_number']; } elseif ($this->smsFrequencyNumber > 0) { $frequencyTime = new \DateInterval('P' . $frequencyRule['sms_frequency_time']); $frequencyNumber = $this->smsFrequencyNumber; } $now->sub($frequencyTime); $sentQuery = $statRepo->getLeadStats($lead->getId(), array('fromDate' => $now)); if (!empty($sentQuery) && count($sentQuery) < $frequencyNumber) { return true; } elseif (empty($sentQuery)) { return true; } return false; }
/** * @param Lead $lead * * @return mixed */ public function getAvatar(Lead $lead) { $preferred = $lead->getPreferredProfileImage(); $socialData = $lead->getSocialCache(); $leadEmail = $lead->getEmail(); if ($preferred == 'custom') { if ($fmtime = filemtime($this->getAvatarPath(true) . '/avatar' . $lead->getId())) { // Append file modified time to ensure the latest is used by browser $img = $this->factory->getHelper('template.assets')->getUrl($this->getAvatarPath() . '/avatar' . $lead->getId() . '?' . $fmtime, null, null, false, true); } } elseif (isset($socialData[$preferred]) && !empty($socialData[$preferred]['profile']['profileImage'])) { $img = $socialData[$preferred]['profile']['profileImage']; } if (empty($img)) { // Default to gravatar if others failed if (!empty($leadEmail)) { $img = $this->factory->getHelper('template.gravatar')->getImage($leadEmail); } else { $img = $this->getDefaultAvatar(); } } return $img; }
/** * @param ObjectManager $manager */ public function load(ObjectManager $manager) { $factory = $this->container->get('mautic.factory'); $leadRepo = $factory->getModel('lead.lead')->getRepository(); $today = new \DateTime(); $leads = CsvHelper::csv_to_array(__DIR__ . '/fakeleaddata.csv'); foreach ($leads as $count => $l) { $key = $count + 1; $lead = new Lead(); $lead->setDateAdded($today); $ipAddress = new IpAddress(); $ipAddress->setIpAddress($l['ip'], $factory->getSystemParameters()); $this->setReference('ipAddress-' . $key, $ipAddress); unset($l['ip']); $lead->addIpAddress($ipAddress); $lead->setOwner($this->getReference('sales-user')); foreach ($l as $col => $val) { $lead->addUpdatedField($col, $val); } $leadRepo->saveEntity($lead); $this->setReference('lead-' . $count, $lead); } }
/** * @param $slot * @param Lead|array $lead * * @return DynamicContent */ public function getSlotContentForLead($slot, $lead) { $qb = $this->em->getConnection()->createQueryBuilder(); $id = $lead instanceof Lead ? $lead->getId() : $lead['id']; $qb->select('dc.id, dc.content')->from(MAUTIC_TABLE_PREFIX . 'dynamic_content', 'dc')->leftJoin('dc', MAUTIC_TABLE_PREFIX . 'dynamic_content_lead_data', 'dcld', 'dcld.dynamic_content_id = dc.id')->andWhere($qb->expr()->eq('dcld.slot', ':slot'))->andWhere($qb->expr()->eq('dcld.lead_id', ':lead_id'))->setParameter('slot', $slot)->setParameter('lead_id', $id)->orderBy('dcld.date_added', 'DESC')->addOrderBy('dcld.id', 'DESC'); return $qb->execute()->fetch(); }
/** * Get the user's social profile data from cache or integrations if indicated * * @param \Mautic\LeadBundle\Entity\Lead $lead * @param array $fields * @param bool $refresh * @param string $specificIntegration * @param bool $persistLead * @param bool $returnSettings * * @return array */ public function getUserProfiles($lead, $fields = array(), $refresh = false, $specificIntegration = null, $persistLead = true, $returnSettings = false) { $socialCache = $lead->getSocialCache(); $featureSettings = array(); if ($refresh) { //regenerate from integrations $now = new DateTimeHelper(); //check to see if there are social profiles activated $socialIntegrations = $this->getIntegrationObjects($specificIntegration, array('public_profile', 'public_activity')); /* @var \MauticPlugin\MauticSocialBundle\Integration\SocialIntegration $sn */ foreach ($socialIntegrations as $integration => $sn) { $settings = $sn->getIntegrationSettings(); $features = $settings->getSupportedFeatures(); $identifierField = $this->getUserIdentifierField($sn, $fields); if ($returnSettings) { $featureSettings[$integration] = $settings->getFeatureSettings(); } if ($identifierField && $settings->isPublished()) { $profile = !isset($socialCache[$integration]) ? array() : $socialCache[$integration]; //clear the cache unset($profile['profile'], $profile['activity']); if (in_array('public_profile', $features) && $sn->isAuthorized()) { $sn->getUserData($identifierField, $profile); } if (in_array('public_activity', $features) && $sn->isAuthorized()) { $sn->getPublicActivity($identifierField, $profile); } if (!empty($profile['profile']) || !empty($profile['activity'])) { if (!isset($socialCache[$integration])) { $socialCache[$integration] = array(); } $socialCache[$integration]['profile'] = !empty($profile['profile']) ? $profile['profile'] : array(); $socialCache[$integration]['activity'] = !empty($profile['activity']) ? $profile['activity'] : array(); $socialCache[$integration]['lastRefresh'] = $now->toUtcString(); } } elseif (isset($socialCache[$integration])) { //integration is now not applicable unset($socialCache[$integration]); } } if ($persistLead && !empty($socialCache)) { $lead->setSocialCache($socialCache); $this->factory->getEntityManager()->getRepository('MauticLeadBundle:Lead')->saveEntity($lead); } } elseif ($returnSettings) { $socialIntegrations = $this->getIntegrationObjects($specificIntegration, array('public_profile', 'public_activity')); foreach ($socialIntegrations as $integration => $sn) { $settings = $sn->getIntegrationSettings(); $featureSettings[$integration] = $settings->getFeatureSettings(); } } if ($specificIntegration) { return $returnSettings ? array(array($specificIntegration => $socialCache[$specificIntegration]), $featureSettings) : array($specificIntegration => $socialCache[$specificIntegration]); } return $returnSettings ? array($socialCache, $featureSettings) : $socialCache; }
/** * Upload an asset * * @param Lead $lead */ private function uploadAvatar(Lead $lead) { $file = $this->request->files->get('lead[custom_avatar]', null, true); $avatarDir = $this->factory->getHelper('template.avatar')->getAvatarPath(true); if (!file_exists($avatarDir)) { mkdir($avatarDir); } $file->move($avatarDir, 'avatar' . $lead->getId()); //remove the file from request $this->request->files->remove('lead'); }
/** * Create or update existing Mautic lead from the integration's profile data * * @param mixed $data Profile data from integration * @param bool|true $persist Set to false to not persist lead to the database in this method * @param array|null $socialCache * @param mixed||null $identifiers * * @return Lead */ public function getMauticLead($data, $persist = true, $socialCache = null, $identifiers = null) { if (is_object($data)) { // Convert to array in all levels $data = json_encode(json_decode($data), true); } elseif (is_string($data)) { // Assume JSON $data = json_decode($data, true); } $config = $this->mergeConfigToFeatureSettings([]); // Match that data with mapped lead fields $matchedFields = $this->populateMauticLeadData($data, $config); if (empty($matchedFields)) { return; } // Find unique identifier fields used by the integration /** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */ $leadModel = $this->factory->getModel('lead'); $uniqueLeadFields = $this->factory->getModel('lead.field')->getUniqueIdentiferFields(); $uniqueLeadFieldData = []; foreach ($matchedFields as $leadField => $value) { if (array_key_exists($leadField, $uniqueLeadFields) && !empty($value)) { $uniqueLeadFieldData[$leadField] = $value; } } // Default to new lead $lead = new Lead(); $lead->setNewlyCreated(true); if (count($uniqueLeadFieldData)) { $existingLeads = $this->factory->getEntityManager()->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields($uniqueLeadFieldData); if (!empty($existingLeads)) { $lead = array_shift($existingLeads); } } $leadModel->setFieldValues($lead, $matchedFields, false, false); if (!empty($socialCache)) { // Update the social cache $leadSocialCache = $lead->getSocialCache(); if (!isset($leadSocialCache[$this->getName()])) { $leadSocialCache[$this->getName()] = []; } $leadSocialCache[$this->getName()] = array_merge($leadSocialCache[$this->getName()], $socialCache); // Check for activity while here if (null !== $identifiers && in_array('public_activity', $this->getSupportedFeatures())) { $this->getPublicActivity($identifiers, $leadSocialCache[$this->getName()]); } $lead->setSocialCache($leadSocialCache); } // Update the internal info integration object that has updated the record if (isset($data['internal'])) { $internalInfo = $lead->getInternal(); $internalInfo[$this->getName()] = $data['internal']; $lead->setInternal($internalInfo); } if ($persist) { // Only persist if instructed to do so as it could be that calling code needs to manipulate the lead prior to executing event listeners $leadModel->saveEntity($lead, false); } return $lead; }
/** * Create/update lead from form submit * * @param $form * @param array $leadFieldMatches * * @return Lead */ protected function createLeadFromSubmit($form, array $leadFieldMatches) { /** @var \Mautic\LeadBundle\Model\LeadModel $model */ $model = $this->factory->getModel('lead'); $em = $this->factory->getEntityManager(); //set the mapped data $leadFields = $this->factory->getModel('lead.field')->getRepository()->getAliases(null, true, false); $inKioskMode = $form->isInKioskMode(); if (!$inKioskMode) { // Default to currently tracked lead $lead = $model->getCurrentLead(); $leadId = $lead->getId(); $currentFields = $model->flattenFields($lead->getFields()); } else { // Default to a new lead in kiosk mode $lead = new Lead(); $lead->setNewlyCreated(true); $currentFields = $leadFieldMatches; $leadId = null; } $uniqueLeadFields = $this->factory->getModel('lead.field')->getUniqueIdentiferFields(); // Closure to get data and unique fields $getData = function ($currentFields, $uniqueOnly = false) use($leadFields, $uniqueLeadFields) { $uniqueFieldsWithData = $data = array(); foreach ($leadFields as $alias) { $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 : array($data, $uniqueFieldsWithData); }; // Closure to help search for a conflict $checkForIdentifierConflict = function ($fieldSet1, $fieldSet2) { // Find conflicts $diff = array_diff($fieldSet1, $fieldSet2); // Remove empty values $diff = array_filter($diff); return count($diff); }; // Get data for the form submission list($data, $uniqueFieldsWithData) = $getData($leadFieldMatches); // Check for duplicate lead /** @var \Mautic\LeadBundle\Entity\LeadRepository $leads */ $leads = !empty($uniqueFieldsWithData) ? $em->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields($uniqueFieldsWithData, $leadId) : array(); $uniqueFieldsCurrent = $getData($currentFields, true); if (count($leads)) { /** @var \Mautic\LeadBundle\Entity\Lead $foundLead */ $foundLead = $leads[0]; // Check for a conflict with the currently tracked lead $foundLeadFields = $model->flattenFields($foundLead->getFields()); // Get unique identifier fields for the found lead then compare with the lead currently tracked $uniqueFieldsFound = $getData($foundLeadFields, true); $hasConflict = $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; } else { // Merge the found lead with currently tracked lead $lead = $model->mergeLeads($lead, $foundLead); } // Update unique fields data for comparison with submitted data $currentFields = $model->flattenFields($lead->getFields()); $uniqueFieldsCurrent = $getData($currentFields, true); } if (!$inKioskMode) { // Check for conflicts with the submitted data and the currently tracked lead $hasConflict = $checkForIdentifierConflict($uniqueFieldsWithData, $uniqueFieldsCurrent); if ($hasConflict) { // There's a conflict so create a new lead $lead = new Lead(); $lead->setNewlyCreated(true); } } //check for existing IP address $ipAddress = $this->factory->getIpAddress(); //no lead was found by a mapped email field so create a new one if ($lead->isNewlyCreated()) { if (!$inKioskMode) { $lead->addIpAddress($ipAddress); } // last active time $lead->setLastActive(new \DateTime()); } elseif (!$inKioskMode) { $leadIpAddresses = $lead->getIpAddresses(); if (!$leadIpAddresses->contains($ipAddress)) { $lead->addIpAddress($ipAddress); } } //set the mapped fields $model->setFieldValues($lead, $data, false); if (!empty($event)) { $event->setIpAddress($ipAddress); $lead->addPointsChangeLog($event); } //create a new lead $model->saveEntity($lead, false); if (!$inKioskMode) { // Set the current lead which will generate tracking cookies $model->setCurrentLead($lead); } else { // Set system current lead which will still allow execution of events without generating tracking cookies $model->setSystemCurrentLead($lead); } return $lead; }
/** * Fetch start/limit for queries. * * @return array */ public function getEventLimit() { return ['leadId' => $this->lead->getId(), 'limit' => $this->limit, 'start' => 1 === $this->page ? 0 : ($this->page - 1) * $this->limit]; }
/** * Modify tags with support to remove via a prefixed minus sign * * @param Lead $lead * @param $tags * @param $removeTags * @param $persist */ public function modifyTags(Lead $lead, $tags, array $removeTags = null, $persist = true) { $leadTags = $lead->getTags(); if (!is_array($tags)) { $tags = explode(',', $tags); } array_walk($tags, create_function('&$val', '$val = trim($val); \\Mautic\\CoreBundle\\Helper\\InputHelper::clean($val);')); // See which tags already exist $foundTags = $this->getTagRepository()->getTagsByName($tags); foreach ($tags as $tag) { if (strpos($tag, '-') === 0) { // Tag to be removed $tag = substr($tag, 1); if (array_key_exists($tag, $foundTags) && $leadTags->contains($foundTags[$tag])) { $lead->removeTag($foundTags[$tag]); } } else { // Tag to be added if (!array_key_exists($tag, $foundTags)) { // New tag $newTag = new Tag(); $newTag->setTag($tag); $lead->addTag($newTag); } elseif (!$leadTags->contains($foundTags[$tag])) { $lead->addTag($foundTags[$tag]); } } } if ($removeTags !== null) { foreach ($removeTags as $tag) { // Tag to be removed if (array_key_exists($tag, $foundTags) && $leadTags->contains($foundTags[$tag])) { $lead->removeTag($foundTags[$tag]); } } } if ($persist) { $this->saveEntity($lead); } }
/** * Create/update lead from form submit * * @param $form * @param array $leadFieldMatches * * @return Lead */ protected function createLeadFromSubmit($form, array $leadFieldMatches) { /** @var \Mautic\LeadBundle\Model\LeadModel $model */ $model = $this->factory->getModel('lead'); $em = $this->factory->getEntityManager(); //set the mapped data $leadFields = $this->factory->getModel('lead.field')->getRepository()->getAliases(null, true, false); $data = array(); $inKioskMode = $form->isInKioskMode(); if (!$inKioskMode) { $lead = $model->getCurrentLead(); $leadId = $lead->getId(); $currentFields = $lead->getFields(); } else { $lead = new Lead(); $lead->setNewlyCreated(true); $leadId = null; } $uniqueLeadFields = $this->factory->getModel('lead.field')->getUniqueIdentiferFields(); $uniqueFieldsWithData = array(); foreach ($leadFields as $alias) { $data[$alias] = ''; if (isset($leadFieldMatches[$alias])) { $value = $leadFieldMatches[$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; } } } //update the lead rather than creating a new one if there is for sure identifier match ($leadId is to exclude lead from getCurrentLead()) /** @var \Mautic\LeadBundle\Entity\LeadRepository $leads */ $leads = !empty($uniqueFieldsWithData) ? $em->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields($uniqueFieldsWithData, $leadId) : array(); if (count($leads)) { //merge with current lead if not in kiosk mode $lead = $inKioskMode ? $leads[0] : $model->mergeLeads($lead, $leads[0]); } elseif (!$inKioskMode) { // Flatten current fields $currentFields = $model->flattenFields($currentFields); // Create a new lead if unique identifiers differ from getCurrentLead() and submitted data foreach ($uniqueLeadFields as $alias => $value) { //create a new lead if details differ $currentValue = $currentFields[$alias]; if (!empty($currentValue) && strtolower($currentValue) != strtolower($value)) { //for sure a different lead so create a new one $lead = new Lead(); $lead->setNewlyCreated(true); break; } } } //check for existing IP address $ipAddress = $this->factory->getIpAddress(); //no lead was found by a mapped email field so create a new one if ($lead->isNewlyCreated()) { if (!$inKioskMode) { $lead->addIpAddress($ipAddress); } // last active time $lead->setLastActive(new \DateTime()); } elseif (!$inKioskMode) { $leadIpAddresses = $lead->getIpAddresses(); if (!$leadIpAddresses->contains($ipAddress)) { $lead->addIpAddress($ipAddress); } } //set the mapped fields $model->setFieldValues($lead, $data, false); if (!empty($event)) { $event->setIpAddress($ipAddress); $lead->addPointsChangeLog($event); } //create a new lead $model->saveEntity($lead, false); if (!$inKioskMode) { // Set the current lead which will generate tracking cookies $model->setCurrentLead($lead); } else { // Set system current lead which will still allow execution of events without generating tracking cookies $model->setSystemCurrentLead($lead); } return $lead; }
/** * Trigger events for the current lead * * @param Lead $lead */ public function triggerEvents(Lead $lead) { $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); if (!empty($events)) { //get a list of actions that has already been applied to this lead $appliedEvents = $repo->getLeadTriggeredEvents($lead->getId()); $ipAddress = $this->factory->getIpAddress(); $persist = array(); 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($this->em->getReference('MauticPointBundle:TriggerEvent', $event['id'])); $log->setLead($lead); $log->setDateFired(new \DateTime()); $persist[] = $log; } } if (!empty($persist)) { $this->getEventRepository()->saveEntities($persist); $this->em->clear('Mautic\\PointBundle\\Entity\\LeadTriggerLog'); $this->em->clear('Mautic\\PointBundle\\Entity\\TriggerEvent'); } } }
/** * Create/update lead from form submit * * @param $form * @param array $leadFieldMatches * * @return Lead */ protected function createLeadFromSubmit($form, array $leadFieldMatches) { /** @var \Mautic\LeadBundle\Model\LeadModel $model */ $model = $this->factory->getModel('lead'); $em = $this->factory->getEntityManager(); $logger = $this->factory->getLogger(); //set the mapped data $leadFields = $this->factory->getModel('lead.field')->getRepository()->getAliases(null, true, false); $inKioskMode = $form->isInKioskMode(); if (!$inKioskMode) { // Default to currently tracked lead $lead = $model->getCurrentLead(); $leadId = $lead->getId(); $currentFields = $model->flattenFields($lead->getFields()); $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; $logger->debug('FORM: In kiosk mode so assuming a new contact'); } $uniqueLeadFields = $this->factory->getModel('lead.field')->getUniqueIdentiferFields(); // Closure to get data and unique fields $getData = function ($currentFields, $uniqueOnly = false) use($leadFields, $uniqueLeadFields) { $uniqueFieldsWithData = $data = array(); foreach ($leadFields as $alias) { $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 : array($data, $uniqueFieldsWithData); }; // Closure to help search for a conflict $checkForIdentifierConflict = function ($fieldSet1, $fieldSet2) use($logger) { // Find fields in both sets $potentialConflicts = array_keys(array_intersect_key($fieldSet1, $fieldSet2)); $logger->debug('FORM: Potential conflicts ' . implode(', ', array_keys($potentialConflicts)) . ' = ' . implode(', ', $potentialConflicts)); $conflicts = array(); foreach ($potentialConflicts as $field) { if (!empty($fieldSet1[$field]) && !empty($fieldSet2[$field])) { if (strtolower($fieldSet1[$field]) !== strtolower($fieldSet2[$field])) { $conflicts[] = $field; } } } return array(count($conflicts), $conflicts); }; // Get data for the form submission list($data, $uniqueFieldsWithData) = $getData($leadFieldMatches); $logger->debug('FORM: Unique fields submitted include ' . implode(', ', $uniqueFieldsWithData)); // Check for duplicate lead /** @var \Mautic\LeadBundle\Entity\LeadRepository $leads */ $leads = !empty($uniqueFieldsWithData) ? $em->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields($uniqueFieldsWithData, $leadId) : array(); $uniqueFieldsCurrent = $getData($currentFields, true); if (count($leads)) { $logger->debug(count($leads) . ' found based on unique identifiers'); /** @var \Mautic\LeadBundle\Entity\Lead $foundLead */ $foundLead = $leads[0]; $logger->debug('FORM: Testing contact ID# ' . $foundLead->getId() . ' for conflicts'); // Check for a conflict with the currently tracked lead $foundLeadFields = $model->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) { $logger->debug('FORM: Conflicts found in ' . implode(', ', $conflicts) . ' so not merging'); } else { $logger->debug('FORM: In kiosk mode so not merging'); } } else { $logger->debug('FORM: Merging contacts ' . $lead->getId() . ' and ' . $foundLead->getId()); // Merge the found lead with currently tracked lead $lead = $model->mergeLeads($lead, $foundLead); } // Update unique fields data for comparison with submitted data $currentFields = $model->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); $logger->debug('FORM: Current unique contact fields ' . implode(', ', array_keys($uniqueFieldsCurrent)) . ' = ' . implode(', ', $uniqueFieldsCurrent)); $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); $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->factory->getIpAddress(); //no lead was found by a mapped email field so create a new one if ($lead->isNewlyCreated()) { if (!$inKioskMode) { $lead->addIpAddress($ipAddress); $logger->debug('FORM: Associating ' . $ipAddress->getIpAddress() . ' to contact'); } } elseif (!$inKioskMode) { $leadIpAddresses = $lead->getIpAddresses(); if (!$leadIpAddresses->contains($ipAddress)) { $lead->addIpAddress($ipAddress); $logger->debug('FORM: Associating ' . $ipAddress->getIpAddress() . ' to contact'); } } //set the mapped fields $model->setFieldValues($lead, $data, false); if (!empty($event)) { $event->setIpAddress($ipAddress); $lead->addPointsChangeLog($event); } // last active time $lead->setLastActive(new \DateTime()); //create a new lead $model->saveEntity($lead, false); if (!$inKioskMode) { // Set the current lead which will generate tracking cookies $model->setCurrentLead($lead); } else { // Set system current lead which will still allow execution of events without generating tracking cookies $model->setSystemCurrentLead($lead); } return $lead; }
/** * Modify tags with support to remove via a prefixed minus sign * * @param Lead $lead * @param $tags * @param $removeTags * @param $persist */ public function modifyTags(Lead $lead, $tags, array $removeTags = null, $persist = true) { $logger = $this->factory->getLogger(); $leadTags = $lead->getTags(); if ($leadTags) { $logger->debug('LEAD: Lead currently has tags ' . implode(', ', $leadTags->getKeys())); } if (!is_array($tags)) { $tags = explode(',', $tags); } $logger->debug('CONTACT: Adding ' . implode(', ', $tags) . ' to contact ID# ' . $lead->getId()); array_walk($tags, create_function('&$val', '$val = trim($val); \\Mautic\\CoreBundle\\Helper\\InputHelper::clean($val);')); // See which tags already exist $foundTags = $this->getTagRepository()->getTagsByName($tags); foreach ($tags as $tag) { if (strpos($tag, '-') === 0) { // Tag to be removed $tag = substr($tag, 1); if (array_key_exists($tag, $foundTags) && $leadTags->contains($foundTags[$tag])) { $lead->removeTag($foundTags[$tag]); $logger->debug('LEAD: Removed ' . $tag); } } else { // Tag to be added if (!array_key_exists($tag, $foundTags)) { // New tag $newTag = new Tag(); $newTag->setTag($tag); $lead->addTag($newTag); $logger->debug('LEAD: Added ' . $tag); } elseif (!$leadTags->contains($foundTags[$tag])) { $lead->addTag($foundTags[$tag]); $logger->debug('LEAD: Added ' . $tag); } } } if (!empty($removeTags)) { $logger->debug('CONTACT: Removing ' . implode(', ', $removeTags) . ' for contact ID# ' . $lead->getId()); array_walk($removeTags, create_function('&$val', '$val = trim($val); \\Mautic\\CoreBundle\\Helper\\InputHelper::clean($val);')); // See which tags really exist $foundRemoveTags = $this->getTagRepository()->getTagsByName($removeTags); foreach ($removeTags as $tag) { // Tag to be removed if (array_key_exists($tag, $foundRemoveTags) && $leadTags->contains($foundRemoveTags[$tag])) { $lead->removeTag($foundRemoveTags[$tag]); $logger->debug('LEAD: Removed ' . $tag); } } } if ($persist) { $this->saveEntity($lead); } }
/** * {@inheritDoc} */ public function __toString() { $this->__initializer__ && $this->__initializer__->__invoke($this, '__toString', array()); return parent::__toString(); }
/** * Determine if this campaign applies * * @param $eventDetails * @param $event * * @return bool */ public static function validateFormValue(MauticFactory $factory, $event, Lead $lead) { if (!$lead || !$lead->getId()) { return false; } $operators = $factory->getModel('lead')->getFilterExpressionFunctions(); return $factory->getModel('lead.field')->getRepository()->compareValue($lead->getId(), $event['properties']['field'], $event['properties']['value'], $operators[$event['properties']['operator']]['expr']); }
/** * Decide if the field should be displayed based on thr progressive profiling conditions * * @param array|null $submissions * @param Lead $lead * @param Form $form * * @return boolean */ public function showForContact($submissions = null, Lead $lead = null, Form $form = null) { // Always show in the kiosk mode if ($form !== null && $form->getInKioskMode() === true) { return true; } // Hide the field if there is the submission count limit and hide it untill the limit is overcame if ($this->showAfterXSubmissions > 0 && $this->showAfterXSubmissions > count($submissions)) { return false; } if ($this->showWhenValueExists === false) { // Hide the field if there is the value condition and if we already know the value for this field if ($submissions) { foreach ($submissions as $submission) { if (!empty($submission[$this->alias])) { return false; } } } // Hide the field if the value is already known from the lead profile if ($lead !== null && $this->leadField && $lead->getFieldValue($this->leadField) !== null) { return false; } } return true; }
/** * Execute or schedule an event. Condition events are executed recursively * * @param array $event * @param Campaign $campaign * @param Lead $lead * @param array $eventSettings * @param bool $allowNegative * @param \DateTime $parentTriggeredDate * @param \DateTime|bool $eventTriggerDate * @param bool $logExists * @param integer $evaluatedEventCount The number of events evaluated for the current method (kickoff, negative/inaction, scheduled) * @param integer $executedEventCount The number of events successfully executed for the current method * @param integer $totalEventCount The total number of events across all methods * * @return bool */ public function executeEvent($event, $campaign, $lead, $eventSettings = null, $allowNegative = false, \DateTime $parentTriggeredDate = null, $eventTriggerDate = null, $logExists = false, &$evaluatedEventCount = 0, &$executedEventCount = 0, &$totalEventCount = 0) { $evaluatedEventCount++; $totalEventCount++; // Get event settings if applicable if ($eventSettings === null) { /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */ $campaignModel = $this->factory->getModel('campaign'); $eventSettings = $campaignModel->getEvents(); } // Set date timing should be compared with if applicable if ($parentTriggeredDate === null) { // Default to today $parentTriggeredDate = new \DateTime(); } $repo = $this->getRepository(); $logger = $this->factory->getLogger(); if (isset($eventSettings[$event['eventType']][$event['type']])) { $thisEventSettings = $eventSettings[$event['eventType']][$event['type']]; } else { $logger->debug('CAMPAIGN: Settings not found for ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId()); unset($event); return false; } if ($event['eventType'] == 'condition') { $allowNegative = true; } // Set campaign ID $event['campaign'] = array('id' => $campaign->getId(), 'name' => $campaign->getName()); // Ensure properties is an array if ($event['properties'] === null) { $event['properties'] = array(); } elseif (!is_array($event['properties'])) { $event['properties'] = unserialize($event['properties']); } // Ensure triggerDate is a \DateTime if ($event['triggerMode'] == 'date' && !$event['triggerDate'] instanceof \DateTime) { $triggerDate = new DateTimeHelper($event['triggerDate']); $event['triggerDate'] = $triggerDate->getDateTime(); unset($triggerDate); } if ($eventTriggerDate == null) { $eventTriggerDate = $this->checkEventTiming($event, $parentTriggeredDate, $allowNegative); } $result = true; // Create/get log entry if ($logExists) { $log = $this->em->getReference('MauticCampaignBundle:LeadEventLog', array('lead' => $lead->getId(), 'event' => $event['id'])); } else { $systemTriggered = !defined('MAUTIC_CAMPAIGN_NOT_SYSTEM_TRIGGERED'); $log = $this->getLogEntity($event['id'], $campaign, $lead, null, $systemTriggered); } if ($eventTriggerDate instanceof \DateTime) { $executedEventCount++; //lead actively triggered this event, a decision wasn't involved, or it was system triggered and a "no" path so schedule the event to be fired at the defined time $logger->debug('CAMPAIGN: ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId() . ' has timing that is not appropriate and thus scheduled for ' . $eventTriggerDate->format('Y-m-d H:m:i T')); $log->setIsScheduled(true); $log->setTriggerDate($eventTriggerDate); $repo->saveEntity($log); if ($this->dispatcher->hasListeners(CampaignEvents::ON_EVENT_SCHEDULED)) { $args = array('eventSettings' => $thisEventSettings, 'eventDetails' => null, 'event' => $event, 'lead' => $lead, 'factory' => $this->factory, 'systemTriggered' => true, 'dateScheduled' => $eventTriggerDate); $scheduledEvent = new CampaignScheduledEvent($args); $this->dispatcher->dispatch(CampaignEvents::ON_EVENT_SCHEDULED, $scheduledEvent); unset($scheduledEvent, $args); } } elseif ($eventTriggerDate) { $wasScheduled = $log->getIsScheduled(); $log->setIsScheduled(false); $log->setTriggerDate(null); $log->setDateTriggered(new \DateTime()); try { $repo->saveEntity($log); } catch (EntityNotFoundException $exception) { // The lead has been likely removed from this lead/list $logger->debug('CAMPAIGN: ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId() . ' wasn\'t found: ' . $exception->getMessage()); return false; } catch (DBALException $exception) { $logger->debug('CAMPAIGN: ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId() . ' failed with DB error: ' . $exception->getMessage()); return false; } //trigger the action $response = $this->invokeEventCallback($event, $thisEventSettings, $lead, null, true, $log); if ($response instanceof LeadEventLog) { // Listener handled the event and returned a log entry $repo->saveEntity($response); $this->em->detach($response); $executedEventCount++; $logger->debug('CAMPAIGN: Listener handled event for ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId()); } elseif ($response === false && $event['eventType'] == 'action') { $result = false; // Something failed if ($wasScheduled) { // Reschedule $log->setIsScheduled(true); $log->setTriggerDate(new \DateTime()); $log->setDateTriggered(null); $repo->saveEntity($log); } else { // Remove $repo->deleteEntity($log); } $logger->debug('CAMPAIGN: ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId() . ' failed with a response of ' . var_export($response, true)); } else { $executedEventCount++; if ($response !== true) { if ($this->triggeredResponses !== false) { if (!is_array($this->triggeredResponses[$event['eventType']])) { $this->triggeredResponses[$event['eventType']] = array(); } $this->triggeredResponses[$event['eventType']][$event['id']] = $response; } $log->setMetadata($response); $repo->saveEntity($log); } $logger->debug('CAMPAIGN: ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId() . ' successfully executed and logged with a response of ' . var_export($response, true)); } $this->handleCondition($response, $eventSettings, $event, $campaign, $lead, $evaluatedEventCount, $executedEventCount, $totalEventCount); } else { //else do nothing $result = false; $logger->debug('CAMPAIGN: Timing failed (' . gettype($eventTriggerDate) . ') for ' . ucfirst($event['eventType']) . ' ID# ' . $event['id'] . ' for contact ID# ' . $lead->getId()); } if (!empty($log)) { // Detach log $this->em->detach($log); unset($log); } unset($eventTriggerDate, $event); return $result; }
/** * @param Lead $lead * @param $useFilters * @return mixed */ public function getNoteCount(Lead $lead, $useFilters = false) { $session = $this->factory->getSession(); $filter = $useFilters ? $session->get('mautic.lead.' . $lead->getId() . '.note.filter', '') : null; $noteType = $useFilters ? $session->get('mautic.lead.' . $lead->getId() . '.notetype.filter', array()) : null; return $this->getRepository()->getNoteCount($lead->getId(), $filter, $noteType); }