/** * Add a delete entry to the audit log. * * @param Events\AssetEvent $event */ public function onAssetDelete(Events\AssetEvent $event) { $asset = $event->getAsset(); $log = ['bundle' => 'asset', 'object' => 'asset', 'objectId' => $asset->deletedId, 'action' => 'delete', 'details' => ['name' => $asset->getTitle()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); //In case of batch delete, this method call remove the uploaded file $asset->removeUpload(); }
/** * @param CampaignExecutionEvent $event */ public function onCampaignTriggerActionChangePoints(CampaignExecutionEvent $event) { if (!$event->checkContext('lead.changepoints')) { return; } $lead = $event->getLead(); $points = $event->getConfig()['points']; $somethingHappened = false; if ($lead !== null && !empty($points)) { $lead->adjustPoints($points); //add a lead point change log $log = new PointsChangeLog(); $log->setDelta($points); $log->setLead($lead); $log->setType('campaign'); $log->setEventName("{$event->getEvent()['campaign']['id']}: {$event->getEvent()['campaign']['name']}"); $log->setActionName("{$event->getEvent()['id']}: {$event->getEvent()['name']}"); $log->setIpAddress($this->ipLookupHelper->getIpAddress()); $log->setDateAdded(new \DateTime()); $lead->addPointsChangeLog($log); $this->leadModel->saveEntity($lead); $somethingHappened = true; } return $event->setResult($somethingHappened); }
/** * 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->ipLookupHelper->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'); } } }
/** * @param Event|int $event * @param Campaign $campaign * @param \Mautic\LeadBundle\Entity\Lead|null $lead * @param \Mautic\CoreBundle\Entity\IpAddress|null $ipAddress * @param bool $systemTriggered * * @return LeadEventLog * * @throws \Doctrine\ORM\ORMException */ public function getLogEntity($event, $campaign, $lead = null, $ipAddress = null, $systemTriggered = false) { $log = new LeadEventLog(); if ($ipAddress == null) { // Lead triggered from system IP $ipAddress = $this->ipLookupHelper->getIpAddress(); } $log->setIpAddress($ipAddress); if (!$event instanceof Event) { $event = $this->em->getReference('MauticCampaignBundle:Event', $event); } $log->setEvent($event); if (!$campaign instanceof Campaign) { $campaign = $this->em->getReference('MauticCampaignBundle:Campaign', $campaign); } $log->setCampaign($campaign); if ($lead == null) { $lead = $this->leadModel->getCurrentLead(); } $log->setLead($lead); $log->setDateTriggered(new \DateTime()); $log->setSystemTriggered($systemTriggered); // Save some RAM for batch processing unset($event, $campaign, $lead); return $log; }
/** * @param Events\LeadMergeEvent $event */ public function onLeadMerge(Events\LeadMergeEvent $event) { $this->em->getRepository('MauticLeadBundle:PointsChangeLog')->updateLead($event->getLoser()->getId(), $event->getVictor()->getId()); $this->em->getRepository('MauticLeadBundle:ListLead')->updateLead($event->getLoser()->getId(), $event->getVictor()->getId()); $this->em->getRepository('MauticLeadBundle:LeadNote')->updateLead($event->getLoser()->getId(), $event->getVictor()->getId()); $log = ['bundle' => 'lead', 'object' => 'lead', 'objectId' => $event->getLoser()->getId(), 'action' => 'merge', 'details' => ['merged_into' => $event->getVictor()->getId()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Record page hit. * * @param $page * @param Request $request * @param string $code * @param Lead|null $lead * @param array $query * * @return Hit $hit * * @throws \Exception */ public function hitPage($page, Request $request, $code = '200', Lead $lead = null, $query = []) { // Don't skew results with user hits if (!$this->security->isAnonymous()) { return; } // Process the query if (empty($query)) { $query = $this->getHitQuery($request, $page); } $hit = new Hit(); $hit->setDateHit(new \Datetime()); // Check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $hit->setIpAddress($ipAddress); // Check for any clickthrough info $clickthrough = []; if (!empty($query['ct'])) { $clickthrough = $query['ct']; if (!is_array($clickthrough)) { $clickthrough = $this->decodeArrayFromUrl($clickthrough); } if (!empty($clickthrough['channel'])) { if (count($clickthrough['channel']) === 1) { $channelId = reset($clickthrough['channel']); $channel = key($clickthrough['channel']); } else { $channel = $clickthrough['channel'][0]; $channelId = (int) $clickthrough['channel'][1]; } $hit->setSource($channel); $hit->setSourceId($channelId); } elseif (!empty($clickthrough['source'])) { $hit->setSource($clickthrough['source'][0]); $hit->setSourceId($clickthrough['source'][1]); } if (!empty($clickthrough['email'])) { $emailRepo = $this->em->getRepository('MauticEmailBundle:Email'); if ($emailEntity = $emailRepo->getEntity($clickthrough['email'])) { $hit->setEmail($emailEntity); } } } // Get lead if required if (null == $lead) { $lead = $this->leadModel->getContactFromRequest($query); } $this->leadModel->saveEntity($lead); // Set info from request $hit->setQuery($query); $hit->setUrl(isset($query['page_url']) ? $query['page_url'] : $request->getRequestUri()); if (isset($query['page_referrer'])) { $hit->setReferer($query['page_referrer']); } if (isset($query['page_language'])) { $hit->setPageLanguage($query['page_language']); } if (isset($query['page_title'])) { $hit->setUrlTitle($query['page_title']); } // Store tracking ID list($trackingId, $trackingNewlyGenerated) = $this->leadModel->getTrackingCookie(); $hit->setTrackingId($trackingId); $hit->setLead($lead); $isUnique = $trackingNewlyGenerated; if (!$trackingNewlyGenerated) { $lastHit = $request->cookies->get('mautic_referer_id'); if (!empty($lastHit)) { //this is not a new session so update the last hit if applicable with the date/time the user left $this->getHitRepository()->updateHitDateLeft($lastHit); } // Check if this is a unique page hit $isUnique = $this->getHitRepository()->isUniquePageHit($page, $trackingId); } if (!empty($page)) { if ($page instanceof Page) { $hit->setPage($page); $hit->setPageLanguage($page->getLanguage()); $isVariant = $isUnique ? $page->getVariantStartDate() : false; try { $this->getRepository()->upHitCount($page->getId(), 1, $isUnique, !empty($isVariant)); } catch (\Exception $exception) { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } elseif ($page instanceof Redirect) { $hit->setRedirect($page); try { $this->pageRedirectModel->getRepository()->upHitCount($page->getId(), 1, $isUnique); // If this is a trackable, up the trackable counts as well if (!empty($clickthrough['channel'])) { $channelId = reset($clickthrough['channel']); $channel = key($clickthrough['channel']); $this->pageTrackableModel->getRepository()->upHitCount($page->getId(), $channel, $channelId, 1, $isUnique); } } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } } } //glean info from the IP address if ($details = $ipAddress->getIpDetails()) { $hit->setCountry($details['country']); $hit->setRegion($details['region']); $hit->setCity($details['city']); $hit->setIsp($details['isp']); $hit->setOrganization($details['organization']); } $hit->setCode($code); if (!$hit->getReferer()) { $hit->setReferer($request->server->get('HTTP_REFERER')); } $hit->setUserAgent($request->server->get('HTTP_USER_AGENT')); $hit->setRemoteHost($request->server->get('REMOTE_HOST')); if ($isUnique) { // Add UTM tags entry if a UTM tag exist $queryHasUtmTags = false; if (!is_array($query)) { parse_str($query, $query); } foreach ($query as $key => $value) { if (strpos($key, 'utm_') !== false) { $queryHasUtmTags = true; break; } } if ($queryHasUtmTags) { $utmTags = new UtmTag(); $utmTags->setDateAdded($hit->getDateHit()); $utmTags->setUrl($hit->getUrl()); $utmTags->setReferer($hit->getReferer()); $utmTags->setQuery($hit->getQuery()); $utmTags->setUserAgent($hit->getUserAgent()); $utmTags->setRemoteHost($hit->getRemoteHost()); $utmTags->setLead($lead); if (key_exists('utm_campaign', $query)) { $utmTags->setUtmCampaign($query['utm_campaign']); } if (key_exists('utm_term', $query)) { $utmTags->setUtmTerm($query['utm_term']); } if (key_exists('utm_content', $query)) { $utmTags->setUtmConent($query['utm_content']); } if (key_exists('utm_medium', $query)) { $utmTags->setUtmMedium($query['utm_medium']); } if (key_exists('utm_source', $query)) { $utmTags->setUtmSource($query['utm_source']); } $repo = $this->em->getRepository('MauticLeadBundle:UtmTag'); $repo->saveEntity($utmTags); $this->leadModel->setUtmTags($lead, $utmTags); } } //get a list of the languages the user prefers $browserLanguages = $request->server->get('HTTP_ACCEPT_LANGUAGE'); if (!empty($browserLanguages)) { $languages = explode(',', $browserLanguages); foreach ($languages as $k => $l) { if ($pos = strpos(';q=', $l) !== false) { //remove weights $languages[$k] = substr($l, 0, $pos); } } $hit->setBrowserLanguages($languages); } //device granularity $dd = new DeviceDetector($request->server->get('HTTP_USER_AGENT')); $dd->parse(); $deviceRepo = $this->leadModel->getDeviceRepository(); $device = $deviceRepo->getDevice(null, $lead, $dd->getDeviceName(), $dd->getBrand(), $dd->getModel()); if (empty($device)) { $device = new LeadDevice(); $device->setClientInfo($dd->getClient()); $device->setDevice($dd->getDeviceName()); $device->setDeviceBrand($dd->getBrand()); $device->setDeviceModel($dd->getModel()); $device->setDeviceOs($dd->getOs()); $device->setDateOpen($hit->getDateHit()); $device->setLead($lead); $this->em->persist($device); } else { $device = $deviceRepo->getEntity($device['id']); } $hit->setDeviceStat($device); // Wrap in a try/catch to prevent deadlock errors on busy servers try { $this->em->persist($hit); $this->em->flush($hit); } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } if ($this->dispatcher->hasListeners(PageEvents::PAGE_ON_HIT)) { $event = new PageHitEvent($hit, $request, $code, $clickthrough, $isUnique); $this->dispatcher->dispatch(PageEvents::PAGE_ON_HIT, $event); } //save hit to the cookie to use to update the exit time $this->cookieHelper->setCookie('mautic_referer_id', $hit->getId()); return $hit; }
/** * Add a delete entry to the audit log. * * @param ReportEvent $event */ public function onReportDelete(ReportEvent $event) { $report = $event->getReport(); $log = ['bundle' => 'report', 'object' => 'report', 'objectId' => $report->deletedId, 'action' => 'delete', 'details' => ['name' => $report->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a delete entry to the audit log. * * @param WebhookEvent $event */ public function onWebhookDelete(WebhookEvent $event) { $webhook = $event->getWebhook(); $log = ['bundle' => 'webhook', 'object' => 'webhook', 'objectId' => $event->getWebhook()->deletedId, 'action' => 'delete', 'details' => ['name' => $webhook->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a role delete entry to the audit log. * * @param Events\ClientEvent $event */ public function onClientDelete(Events\ClientEvent $event) { $client = $event->getClient(); $log = ['bundle' => 'api', 'object' => 'client', 'objectId' => $client->deletedId, 'action' => 'delete', 'details' => ['name' => $client->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * @param $asset * @param null $request * @param string $code * @param array $systemEntry * * @throws \Doctrine\ORM\ORMException * @throws \Exception */ public function trackDownload($asset, $request = null, $code = '200', $systemEntry = []) { // Don't skew results with in-house downloads if (empty($systemEntry) && !$this->security->isAnonymous()) { return; } if ($request == null) { $request = $this->request; } $download = new Download(); $download->setDateDownload(new \Datetime()); // Download triggered by lead if (empty($systemEntry)) { //check for any clickthrough info $clickthrough = $request->get('ct', false); if (!empty($clickthrough)) { $clickthrough = $this->decodeArrayFromUrl($clickthrough); if (!empty($clickthrough['lead'])) { $lead = $this->leadModel->getEntity($clickthrough['lead']); if ($lead !== null) { $this->leadModel->setLeadCookie($clickthrough['lead']); list($trackingId, $trackingNewlyGenerated) = $this->leadModel->getTrackingCookie(); $leadClickthrough = true; $this->leadModel->setCurrentLead($lead); } } if (!empty($clickthrough['channel'])) { if (count($clickthrough['channel']) === 1) { $channelId = reset($clickthrough['channel']); $channel = key($clickthrough['channel']); } else { $channel = $clickthrough['channel'][0]; $channelId = (int) $clickthrough['channel'][1]; } $download->setSource($channel); $download->setSourceId($channelId); } elseif (!empty($clickthrough['source'])) { $download->setSource($clickthrough['source'][0]); $download->setSourceId($clickthrough['source'][1]); } if (!empty($clickthrough['email'])) { $emailRepo = $this->em->getRepository('MauticEmailBundle:Email'); if ($emailEntity = $emailRepo->getEntity($clickthrough['email'])) { $download->setEmail($emailEntity); } } } if (empty($leadClickthrough)) { list($lead, $trackingId, $trackingNewlyGenerated) = $this->leadModel->getCurrentLead(true); } $download->setLead($lead); } else { $trackingId = ''; if (isset($systemEntry['lead'])) { $lead = $systemEntry['lead']; if (!$lead instanceof Lead) { $leadId = is_array($lead) ? $lead['id'] : $lead; $lead = $this->em->getReference('MauticLeadBundle:Lead', $leadId); } $download->setLead($lead); } if (!empty($systemEntry['source'])) { $download->setSource($systemEntry['source'][0]); $download->setSourceId($systemEntry['source'][1]); } if (isset($systemEntry['email'])) { $email = $systemEntry['email']; if (!$email instanceof Email) { $emailId = is_array($email) ? $email['id'] : $email; $email = $this->em->getReference('MauticEmailBundle:Email', $emailId); } $download->setEmail($email); } if (isset($systemEntry['tracking_id'])) { $trackingId = $systemEntry['tracking_id']; $trackingNewlyGenerated = false; } elseif ($this->security->isAnonymous() && !defined('IN_MAUTIC_CONSOLE')) { // If the session is anonymous and not triggered via CLI, assume the lead did something to trigger the // system forced download such as an email list($trackingId, $trackingNewlyGenerated) = $this->leadModel->getTrackingCookie(); } } $isUnique = true; if (!empty($trackingNewlyGenerated)) { // Cookie was just generated so this is definitely a unique download $isUnique = $trackingNewlyGenerated; } elseif (!empty($trackingId)) { // Determine if this is a unique download $isUnique = $this->getDownloadRepository()->isUniqueDownload($asset->getId(), $trackingId); } $download->setTrackingId($trackingId); if (!empty($asset) && empty($systemEntry)) { $download->setAsset($asset); $this->getRepository()->upDownloadCount($asset->getId(), 1, $isUnique); } //check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $download->setCode($code); $download->setIpAddress($ipAddress); if ($request !== null) { $download->setReferer($request->server->get('HTTP_REFERER')); } // Dispatch event if ($this->dispatcher->hasListeners(AssetEvents::ASSET_ON_LOAD)) { $event = new AssetLoadEvent($download, $isUnique); $this->dispatcher->dispatch(AssetEvents::ASSET_ON_LOAD, $event); } // Wrap in a try/catch to prevent deadlock errors on busy servers try { $this->em->persist($download); $this->em->flush($download); } catch (\Exception $e) { if (MAUTIC_ENV === 'dev') { throw $e; } else { error_log($e); } } $this->em->detach($download); }
/** * Triggers a specific point change * * @param $type * @param mixed $eventDetails passthrough from function triggering action to the callback function * @param mixed $typeId Something unique to the triggering event to prevent unnecessary duplicate calls * @param Lead $lead *25 * @return void */ public function triggerAction($type, $eventDetails = null, $typeId = null, Lead $lead = null) { //only trigger actions for anonymous users if (!$this->security->isAnonymous()) { return; } if ($typeId !== null && MAUTIC_ENV === 'prod') { //let's prevent some unnecessary DB calls $triggeredEvents = $this->session->get('mautic.triggered.point.actions', array()); if (in_array($typeId, $triggeredEvents)) { return; } $triggeredEvents[] = $typeId; $this->session->set('mautic.triggered.point.actions', $triggeredEvents); } //find all the actions for published points /** @var \Mautic\PointBundle\Entity\PointRepository $repo */ $repo = $this->getRepository(); $availablePoints = $repo->getPublishedByType($type); $ipAddress = $this->ipLookupHelper->getIpAddress(); if (null === $lead) { $lead = $this->leadModel->getCurrentLead(); if (null === $lead || !$lead->getId()) { return; } } //get available actions $availableActions = $this->getPointActions(); //get a list of actions that has already been performed on this lead $completedActions = $repo->getCompletedLeadActions($type, $lead->getId()); $persist = array(); foreach ($availablePoints as $action) { //if it's already been done, then skip it if (isset($completedActions[$action->getId()])) { continue; } //make sure the action still exists if (!isset($availableActions['actions'][$action->getType()])) { continue; } $settings = $availableActions['actions'][$action->getType()]; $args = array('action' => array('id' => $action->getId(), 'type' => $action->getType(), 'name' => $action->getName(), 'properties' => $action->getProperties(), 'points' => $action->getDelta()), 'lead' => $lead, 'factory' => $this->factory, 'eventDetails' => $eventDetails); $callback = isset($settings['callback']) ? $settings['callback'] : array('\\Mautic\\PointBundle\\Helper\\EventHelper', 'engagePointAction'); if (is_callable($callback)) { if (is_array($callback)) { $reflection = new \ReflectionMethod($callback[0], $callback[1]); } elseif (strpos($callback, '::') !== false) { $parts = explode('::', $callback); $reflection = new \ReflectionMethod($parts[0], $parts[1]); } else { $reflection = new \ReflectionMethod(null, $callback); } $pass = array(); foreach ($reflection->getParameters() as $param) { if (isset($args[$param->getName()])) { $pass[] = $args[$param->getName()]; } else { $pass[] = null; } } $pointsChange = $reflection->invokeArgs($this, $pass); if ($pointsChange) { $delta = $action->getDelta(); $lead->adjustPoints($delta); $parsed = explode('.', $action->getType()); $lead->addPointsChangeLogEntry($parsed[0], $action->getId() . ": " . $action->getName(), $parsed[1], $delta, $ipAddress); $event = new PointActionEvent($action, $lead); $this->dispatcher->dispatch(PointEvents::POINT_ON_ACTION, $event); $log = new LeadPointLog(); $log->setIpAddress($ipAddress); $log->setPoint($action); $log->setLead($lead); $log->setDateFired(new \DateTime()); $persist[] = $log; } } } if (!empty($persist)) { $this->leadModel->saveEntity($lead); $this->getRepository()->saveEntities($persist); // Detach logs to reserve memory $this->em->clear('Mautic\\PointBundle\\Entity\\LeadPointLog'); } }
/** * Add a delete entry to the audit log. * * @param Events\PageEvent $event */ public function onPageDelete(Events\PageEvent $event) { $page = $event->getPage(); $log = ['bundle' => 'page', 'object' => 'page', 'objectId' => $page->deletedId, 'action' => 'delete', 'details' => ['name' => $page->getTitle()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a role delete entry to the audit log. * * @param Events\RoleEvent $event */ public function onRoleDelete(Events\RoleEvent $event) { $role = $event->getRole(); $log = ['bundle' => 'user', 'object' => 'role', 'objectId' => $role->deletedId, 'action' => 'delete', 'details' => ['name' => $role->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a delete entry to the audit log. * * @param Events\TriggerEvent $event */ public function onTriggerDelete(Events\TriggerEvent $event) { $trigger = $event->getTrigger(); $log = ['bundle' => 'point', 'object' => 'trigger', 'objectId' => $trigger->deletedId, 'action' => 'delete', 'details' => ['name' => $trigger->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Create/update lead from form submit. * * @param $form * @param array $leadFieldMatches * * @return Lead */ protected function createLeadFromSubmit($form, array $leadFieldMatches, $leadFields) { //set the mapped data $inKioskMode = $form->isInKioskMode(); if (!$inKioskMode) { // Default to currently tracked lead $lead = $this->leadModel->getCurrentLead(); $leadId = $lead->getId(); $currentFields = $this->leadModel->flattenFields($lead->getFields()); $this->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; $this->logger->debug('FORM: In kiosk mode so assuming a new contact'); } $uniqueLeadFields = $this->leadFieldModel->getUniqueIdentiferFields(); // Closure to get data and unique fields $getData = function ($currentFields, $uniqueOnly = false) use($leadFields, $uniqueLeadFields) { $uniqueFieldsWithData = $data = []; foreach ($leadFields as $alias => $properties) { $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 : [$data, $uniqueFieldsWithData]; }; // Closure to help search for a conflict $checkForIdentifierConflict = function ($fieldSet1, $fieldSet2) { // Find fields in both sets $potentialConflicts = array_keys(array_intersect_key($fieldSet1, $fieldSet2)); $this->logger->debug('FORM: Potential conflicts ' . implode(', ', array_keys($potentialConflicts)) . ' = ' . implode(', ', $potentialConflicts)); $conflicts = []; foreach ($potentialConflicts as $field) { if (!empty($fieldSet1[$field]) && !empty($fieldSet2[$field])) { if (strtolower($fieldSet1[$field]) !== strtolower($fieldSet2[$field])) { $conflicts[] = $field; } } } return [count($conflicts), $conflicts]; }; // Get data for the form submission list($data, $uniqueFieldsWithData) = $getData($leadFieldMatches); $this->logger->debug('FORM: Unique fields submitted include ' . implode(', ', $uniqueFieldsWithData)); // Check for duplicate lead /** @var \Mautic\LeadBundle\Entity\Lead[] $leads */ $leads = !empty($uniqueFieldsWithData) ? $this->em->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields($uniqueFieldsWithData, $leadId) : []; $uniqueFieldsCurrent = $getData($currentFields, true); if (count($leads)) { $this->logger->debug(count($leads) . ' found based on unique identifiers'); /** @var \Mautic\LeadBundle\Entity\Lead $foundLead */ $foundLead = $leads[0]; $this->logger->debug('FORM: Testing contact ID# ' . $foundLead->getId() . ' for conflicts'); // Check for a conflict with the currently tracked lead $foundLeadFields = $this->leadModel->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) { $this->logger->debug('FORM: Conflicts found in ' . implode(', ', $conflicts) . ' so not merging'); } else { $this->logger->debug('FORM: In kiosk mode so not merging'); } } else { $this->logger->debug('FORM: Merging contacts ' . $lead->getId() . ' and ' . $foundLead->getId()); // Merge the found lead with currently tracked lead $lead = $this->leadModel->mergeLeads($lead, $foundLead); } // Update unique fields data for comparison with submitted data $currentFields = $this->leadModel->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); $this->logger->debug('FORM: Current unique contact fields ' . implode(', ', array_keys($uniqueFieldsCurrent)) . ' = ' . implode(', ', $uniqueFieldsCurrent)); $this->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); $this->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->ipLookupHelper->getIpAddress(); //no lead was found by a mapped email field so create a new one if ($lead->isNewlyCreated()) { if (!$inKioskMode) { $lead->addIpAddress($ipAddress); $this->logger->debug('FORM: Associating ' . $ipAddress->getIpAddress() . ' to contact'); } } elseif (!$inKioskMode) { $leadIpAddresses = $lead->getIpAddresses(); if (!$leadIpAddresses->contains($ipAddress)) { $lead->addIpAddress($ipAddress); $this->logger->debug('FORM: Associating ' . $ipAddress->getIpAddress() . ' to contact'); } } //set the mapped fields $this->leadModel->setFieldValues($lead, $data, false); if (!empty($event)) { $event->setIpAddress($ipAddress); $lead->addPointsChangeLog($event); } // last active time $lead->setLastActive(new \DateTime()); //create a new lead $this->leadModel->saveEntity($lead, false); if (!$inKioskMode) { // Set the current lead which will generate tracking cookies $this->leadModel->setCurrentLead($lead); } else { // Set system current lead which will still allow execution of events without generating tracking cookies $this->leadModel->setSystemCurrentLead($lead); } return $lead; }
/** * Add a company delete entry to the audit log. * * @param Events\CompanyEvent $event */ public function onCompanyDelete(Events\CompanyEvent $event) { $company = $event->getCompany(); $log = ['bundle' => 'lead', 'object' => 'field', 'objectId' => $company->deletedId, 'action' => 'delete', 'details' => ['name', $company->getPrimaryIdentifier()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * @param $fields * @param $data * @param null $owner * @param null $list * @param null $tags * @param bool $persist Persist to the database; otherwise return entity * * @return bool * @throws \Doctrine\ORM\ORMException * @throws \Swift_RfcComplianceException */ public function importLead($fields, $data, $owner = null, $list = null, $tags = null, $persist = true) { // Let's check for an existing lead by email $hasEmail = !empty($fields['email']) && !empty($data[$fields['email']]); if ($hasEmail) { // Validate the email MailHelper::validateEmail($data[$fields['email']]); $leadFound = $this->getRepository()->getLeadByEmail($data[$fields['email']]); $lead = $leadFound ? $this->em->getReference('MauticLeadBundle:Lead', $leadFound['id']) : new Lead(); $merged = $leadFound; } else { $lead = new Lead(); $merged = false; } if (!empty($fields['dateAdded']) && !empty($data[$fields['dateAdded']])) { $dateAdded = new DateTimeHelper($data[$fields['dateAdded']]); $lead->setDateAdded($dateAdded->getUtcDateTime()); } unset($fields['dateAdded']); if (!empty($fields['dateModified']) && !empty($data[$fields['dateModified']])) { $dateModified = new DateTimeHelper($data[$fields['dateModified']]); $lead->setDateModified($dateModified->getUtcDateTime()); } unset($fields['dateModified']); if (!empty($fields['lastActive']) && !empty($data[$fields['lastActive']])) { $lastActive = new DateTimeHelper($data[$fields['lastActive']]); $lead->setLastActive($lastActive->getUtcDateTime()); } unset($fields['lastActive']); if (!empty($fields['dateIdentified']) && !empty($data[$fields['dateIdentified']])) { $dateIdentified = new DateTimeHelper($data[$fields['dateIdentified']]); $lead->setDateIdentified($dateIdentified->getUtcDateTime()); } unset($fields['dateIdentified']); if (!empty($fields['createdByUser']) && !empty($data[$fields['createdByUser']])) { $userRepo = $this->em->getRepository('MauticUserBundle:User'); $createdByUser = $userRepo->findByIdentifier($data[$fields['createdByUser']]); if ($createdByUser !== null) { $lead->setCreatedBy($createdByUser); } } unset($fields['createdByUser']); if (!empty($fields['modifiedByUser']) && !empty($data[$fields['modifiedByUser']])) { $userRepo = $this->em->getRepository('MauticUserBundle:User'); $modifiedByUser = $userRepo->findByIdentifier($data[$fields['modifiedByUser']]); if ($modifiedByUser !== null) { $lead->setModifiedBy($modifiedByUser); } } unset($fields['modifiedByUser']); if (!empty($fields['ip']) && !empty($data[$fields['ip']])) { $addresses = explode(',', $data[$fields['ip']]); foreach ($addresses as $address) { $ipAddress = new IpAddress(); $ipAddress->setIpAddress(trim($address)); $lead->addIpAddress($ipAddress); } } unset($fields['ip']); if (!empty($fields['points']) && !empty($data[$fields['points']]) && $lead->getId() === null) { // Add points only for new leads $lead->setPoints($data[$fields['points']]); //add a lead point change log $log = new PointsChangeLog(); $log->setDelta($data[$fields['points']]); $log->setLead($lead); $log->setType('lead'); $log->setEventName($this->translator->trans('mautic.lead.import.event.name')); $log->setActionName($this->translator->trans('mautic.lead.import.action.name', array('%name%' => $this->user->getUsername()))); $log->setIpAddress($this->ipLookupHelper->getIpAddress()); $log->setDateAdded(new \DateTime()); $lead->addPointsChangeLog($log); } if (!empty($fields['stage']) && !empty($data[$fields['stage']])) { static $stages = []; $stageName = $data[$fields['stage']]; if (!array_key_exists($stageName, $stages)) { // Set stage for contact $stage = $this->em->getRepository('MauticStageBundle:Stage')->getStageByName($stageName); if (empty($stage)) { $stage = new Stage(); $stage->setName($stageName); $stages[$stageName] = $stage; } } else { $stage = $stages[$stageName]; } $lead->setStage($stage); //add a contact stage change log $log = new StagesChangeLog(); $log->setEventName($stage->getId() . ":" . $stage->getName()); $log->setLead($lead); $log->setActionName($this->translator->trans('mautic.lead.import.action.name', ['%name%' => $this->user->getUsername()])); $log->setDateAdded(new \DateTime()); $lead->stageChangeLog($log); } unset($fields['stage']); // Set unsubscribe status if (!empty($fields['doNotEmail']) && !empty($data[$fields['doNotEmail']]) && $hasEmail) { $doNotEmail = filter_var($data[$fields['doNotEmail']], FILTER_VALIDATE_BOOLEAN); if ($doNotEmail) { $reason = $this->translator->trans('mautic.lead.import.by.user', array("%user%" => $this->user->getUsername())); // The email must be set for successful unsubscribtion $lead->addUpdatedField('email', $data[$fields['email']]); $this->addDncForLead($lead, 'email', $reason, DoNotContact::MANUAL); } } unset($fields['doNotEmail']); if ($owner !== null) { $lead->setOwner($this->em->getReference('MauticUserBundle:User', $owner)); } if ($tags !== null) { $this->modifyTags($lead, $tags, null, false); } // Set profile data using the form so that values are validated $fieldData = []; foreach ($fields as $leadField => $importField) { // Prevent overwriting existing data with empty data if (array_key_exists($importField, $data) && !is_null($data[$importField]) && $data[$importField] != '') { $fieldData[$leadField] = $data[$importField]; } } static $leadFields; if (null === $leadFields) { $leadFields = $this->leadFieldModel->getEntities(array('force' => array(array('column' => 'f.isPublished', 'expr' => 'eq', 'value' => true)), 'hydration_mode' => 'HYDRATE_ARRAY')); } $form = $this->createForm($lead, $this->formFactory, null, ['fields' => $leadFields, 'csrf_protection' => false]); // Unset stage and owner from the form because it's already been handled unset($form['stage'], $form['owner']); $form->submit($fieldData); if (!$form->isValid()) { $fieldErrors = []; foreach ($form as $formField) { $errors = $formField->getErrors(true); if (count($errors)) { $errorString = $formField->getConfig()->getOption('label') . ": "; foreach ($errors as $error) { $errorString .= " {$error->getMessage()}"; } $fieldErrors[] = $errorString; } } $fieldErrors = implode("\n", $fieldErrors); throw new \Exception($fieldErrors); } else { // All clear foreach ($fieldData as $field => $value) { $lead->addUpdatedField($field, $value); } } $lead->imported = true; if ($persist) { $this->saveEntity($lead); if ($list !== null) { $this->addToLists($lead, array($list)); } } return $merged; }
/** * @param string|Stat $stat * @param $request * @param bool $viaBrowser */ public function hitEmail($stat, $request, $viaBrowser = false) { if (!$stat instanceof Stat) { $stat = $this->getEmailStatus($stat); } if (!$stat) { return; } $email = $stat->getEmail(); if ((int) $stat->isRead()) { if ($viaBrowser && !$stat->getViewedInBrowser()) { //opened via browser so note it $stat->setViewedInBrowser($viaBrowser); } } $readDateTime = new DateTimeHelper(); $stat->setLastOpened($readDateTime->getDateTime()); $lead = $stat->getLead(); if ($lead !== null) { // Set the lead as current lead $this->leadModel->setCurrentLead($lead); } $firstTime = false; if (!$stat->getIsRead()) { $firstTime = true; $stat->setIsRead(true); $stat->setDateRead($readDateTime->getDateTime()); // Only up counts if associated with both an email and lead if ($email && $lead) { try { $this->getRepository()->upCount($email->getId(), 'read', 1, $email->isVariant()); } catch (\Exception $exception) { error_log($exception); } } } if ($viaBrowser) { $stat->setViewedInBrowser($viaBrowser); } $stat->addOpenDetails(['datetime' => $readDateTime->toUtcString(), 'useragent' => $request->server->get('HTTP_USER_AGENT'), 'inBrowser' => $viaBrowser]); //check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $stat->setIpAddress($ipAddress); if ($this->dispatcher->hasListeners(EmailEvents::EMAIL_ON_OPEN)) { $event = new EmailOpenEvent($stat, $request, $firstTime); $this->dispatcher->dispatch(EmailEvents::EMAIL_ON_OPEN, $event); } //device granularity $dd = new DeviceDetector($request->server->get('HTTP_USER_AGENT')); $dd->parse(); $deviceRepo = $this->leadModel->getDeviceRepository(); $emailOpenDevice = $deviceRepo->getDevice(null, $lead, $dd->getDeviceName(), $dd->getBrand(), $dd->getModel()); if (empty($emailOpenDevice)) { $emailOpenDevice = new LeadDevice(); $emailOpenDevice->setClientInfo($dd->getClient()); $emailOpenDevice->setDevice($dd->getDeviceName()); $emailOpenDevice->setDeviceBrand($dd->getBrand()); $emailOpenDevice->setDeviceModel($dd->getModel()); $emailOpenDevice->setDeviceOs($dd->getOs()); $emailOpenDevice->setDateOpen($readDateTime->toUtcString()); $emailOpenDevice->setLead($lead); try { $this->em->persist($emailOpenDevice); $this->em->flush($emailOpenDevice); } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } } else { $emailOpenDevice = $deviceRepo->getEntity($emailOpenDevice['id']); } if ($email) { $this->em->persist($email); $this->em->flush($email); } if (isset($emailOpenDevice) and is_object($emailOpenDevice)) { $emailOpenStat = new StatDevice(); $emailOpenStat->setIpAddress($ipAddress); $emailOpenStat->setDevice($emailOpenDevice); $emailOpenStat->setDateOpened($readDateTime->toUtcString()); $emailOpenStat->setStat($stat); $this->em->persist($emailOpenStat); $this->em->flush($emailOpenStat); } $this->em->persist($stat); $this->em->flush(); }
/** * @param Request $request * @param string $code * * @throws \Doctrine\ORM\ORMException * @throws \Exception */ public function hitVideo($request, $code = '200') { //don't skew results with in-house hits if (!$this->security->isAnonymous()) { //return; } $lead = $this->leadModel->getCurrentLead(); $guid = $request->get('guid'); $hit = $this->getHitForLeadByGuid($lead, $guid); $hit->setGuid($guid); $hit->setDateHit(new \Datetime()); $hit->setDuration($request->get('duration')); $hit->setUrl($request->get('url')); $hit->setTimeWatched($request->get('total_watched')); //check for existing IP $ipAddress = $this->ipLookupHelper->getIpAddress(); $hit->setIpAddress($ipAddress); // Store query array $query = $request->query->all(); unset($query['d']); $hit->setQuery($query); $hit->setLead($lead); //glean info from the IP address if ($details = $ipAddress->getIpDetails()) { $hit->setCountry($details['country']); $hit->setRegion($details['region']); $hit->setCity($details['city']); $hit->setIsp($details['isp']); $hit->setOrganization($details['organization']); } $hit->setCode($code); if (!$hit->getReferer()) { $hit->setReferer($request->server->get('HTTP_REFERER')); } $hit->setUserAgent($request->server->get('HTTP_USER_AGENT')); $hit->setRemoteHost($request->server->get('REMOTE_HOST')); //get a list of the languages the user prefers $browserLanguages = $request->server->get('HTTP_ACCEPT_LANGUAGE'); if (!empty($browserLanguages)) { $languages = explode(',', $browserLanguages); foreach ($languages as $k => $l) { if ($pos = strpos(';q=', $l) !== false) { //remove weights $languages[$k] = substr($l, 0, $pos); } } $hit->setBrowserLanguages($languages); } // Wrap in a try/catch to prevent deadlock errors on busy servers try { $this->em->persist($hit); $this->em->flush($hit); } catch (\Exception $exception) { if (MAUTIC_ENV === 'dev') { throw $exception; } else { $this->logger->addError($exception->getMessage(), ['exception' => $exception]); } } if ($this->dispatcher->hasListeners(PageEvents::VIDEO_ON_HIT)) { $event = new VideoHitEvent($hit, $request, $code); $this->dispatcher->dispatch(PageEvents::VIDEO_ON_HIT, $event); } }
/** * Add a delete entry to the audit log. * * @param Events\EmailEvent $event */ public function onEmailDelete(Events\EmailEvent $event) { $email = $event->getEmail(); $log = ['bundle' => 'email', 'object' => 'email', 'objectId' => $email->deletedId, 'action' => 'delete', 'details' => ['name' => $email->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a delete entry to the audit log. * * @param Events\CategoryEvent $event */ public function onCategoryDelete(Events\CategoryEvent $event) { $category = $event->getCategory(); $log = ['bundle' => 'category', 'object' => 'category', 'objectId' => $category->deletedId, 'action' => 'delete', 'details' => ['name' => $category->getTitle()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a delete entry to the audit log. * * @param Events\FormEvent $event */ public function onFormDelete(Events\FormEvent $event) { $form = $event->getForm(); $log = ['bundle' => 'form', 'object' => 'form', 'objectId' => $form->deletedId, 'action' => 'delete', 'details' => ['name' => $form->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }
/** * Add a delete entry to the audit log. * * @param FocusEvent $event */ public function onFocusDelete(FocusEvent $event) { $entity = $event->getFocus(); $log = ['bundle' => 'focus', 'object' => 'focus', 'objectId' => $entity->deletedId, 'action' => 'delete', 'details' => ['name' => $entity->getName()], 'ipAddress' => $this->ipHelper->getIpAddressFromRequest()]; $this->auditLogModel->writeToLog($log); }