/** * @param \DateTime $value * @param AbstractPlatform $platform * * @return string|null */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } if (!self::$utc) { self::$utc = new \DateTimeZone('UTC'); } if (!is_object($value)) { $dateHelper = new DateTimeHelper($value); $value = $dateHelper->getDateTime(); } $tz = $value->getTimeZone(); $utcDatetime = new \DateTime($value->format($platform->getDateTimeFormatString()), $tz); $utcDatetime->setTimezone(self::$utc); return $utcDatetime->format($platform->getDateTimeFormatString()); }
/** * {@inheritdoc} * * @param \Mautic\LeadBundle\Entity\Lead &$entity * @param $parameters * @param $form * @param string $action */ protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit') { //Since the request can be from 3rd party, check for an IP address if included if (isset($parameters['ipAddress'])) { $ip = $parameters['ipAddress']; unset($parameters['ipAddress']); $ipAddress = $this->factory->getIpAddress($ip); if (!$entity->getIpAddresses()->contains($ipAddress)) { $entity->addIpAddress($ipAddress); } } // Check for tag string if (isset($parameters['tags'])) { $this->model->modifyTags($entity, $parameters['tags']); unset($parameters['tags']); } if (isset($parameters['companies'])) { $this->model->modifyCompanies($entity, $parameters['companies']); unset($parameters['companies']); } // Check for lastActive date if (isset($parameters['lastActive'])) { $lastActive = new DateTimeHelper($parameters['lastActive']); $entity->setLastActive($lastActive->getDateTime()); } //set the custom field values //pull the data from the form in order to apply the form's formatting foreach ($form as $f) { $parameters[$f->getName()] = $f->getData(); } $this->model->setFieldValues($entity, $parameters, true); }
/** * Fetch the events. * * @return array Events sorted by timestamp with most recent event first */ public function getEvents() { if (empty($this->events)) { return []; } $events = call_user_func_array('array_merge', $this->events); foreach ($events as &$e) { if (!$e['timestamp'] instanceof \DateTime) { $dt = new DateTimeHelper($e['timestamp'], 'Y-m-d H:i:s', 'UTC'); $e['timestamp'] = $dt->getDateTime(); unset($dt); } } if (!empty($this->orderBy)) { usort($events, function ($a, $b) { switch ($this->orderBy[0]) { case 'eventLabel': $aLabel = ''; if (isset($a['eventLabel'])) { $aLabel = is_array($a['eventLabel']) ? $a['eventLabel']['label'] : $a['eventLabel']; } $bLabel = ''; if (isset($b['eventLabel'])) { $bLabel = is_array($b['eventLabel']) ? $b['eventLabel']['label'] : $b['eventLabel']; } return strnatcmp($aLabel, $bLabel); case 'timestamp': if ($a['timestamp'] == $b['timestamp']) { $aPriority = isset($a['eventPriority']) ? (int) $a['eventPriority'] : 0; $bPriority = isset($b['eventPriority']) ? (int) $b['eventPriority'] : 0; return $aPriority - $bPriority; } return $a['timestamp'] < $b['timestamp'] ? -1 : 1; } }); if ($this->orderBy[1] == 'DESC') { $events = array_reverse($events); } } return $events; }
/** * Fetch the events * * @return array Events sorted by timestamp with most recent event first */ public function getEvents($returnGrouped = false) { $events = $this->events; $byDate = array(); // Group by date foreach ($events as $e) { if (!$e['timestamp'] instanceof \DateTime) { $dt = new DateTimeHelper($e['timestamp'], 'Y-m-d H:i:s', 'UTC'); $e['timestamp'] = $dt->getDateTime(); unset($dt); } $dateString = $e['timestamp']->format('Y-m-d H:i'); if (!isset($byDate[$dateString])) { $byDate[$dateString] = array(); } $byDate[$dateString][] = $e; } // Sort by date krsort($byDate); // Sort by certain event actions $order = array('lead.ipadded', 'page.hit', 'form.submitted', 'asset.download', 'lead.merge', 'lead.create', 'lead.identified'); $events = array(); foreach ($byDate as $date => $dateEvents) { usort($dateEvents, function ($a, $b) use($order) { if (!in_array($a['event'], $order) || !in_array($b['event'], $order)) { // No specific order so push to the end return 1; } $pos_a = array_search($a['event'], $order); $pos_b = array_search($b['event'], $order); return $pos_a - $pos_b; }); $byDate[$date] = $dateEvents; $events = array_merge($events, array_reverse($dateEvents)); } return $returnGrouped ? $byDate : $events; }
/** * @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(); }
/** * Check to see if the interval between events are appropriate to fire currentEvent * * @param $action * @param \DateTime $parentTriggeredDate * @param bool $allowNegative * * @return bool */ public function checkEventTiming($action, \DateTime $parentTriggeredDate = null, $allowNegative = false) { $logger = $this->factory->getLogger(); $now = new \DateTime(); $logger->debug('CAMPAIGN: Check timing for ' . ucfirst($action['eventType']) . ' ID# ' . $action['id']); if ($action instanceof Event) { $action = $action->convertToArray(); } if ($action['decisionPath'] == 'no' && !$allowNegative) { $logger->debug('CAMPAIGN: ' . ucfirst($action['eventType']) . ' is attached to a negative path which is not allowed'); return false; } else { $negate = $action['decisionPath'] == 'no' && $allowNegative; if ($action['triggerMode'] == 'interval') { $triggerOn = $negate ? clone $parentTriggeredDate : new \DateTime(); if ($triggerOn == null) { $triggerOn = new \DateTime(); } $interval = $action['triggerInterval']; $unit = strtoupper($action['triggerIntervalUnit']); $logger->debug('CAMPAIGN: Adding interval of ' . $interval . $unit . ' to ' . $triggerOn->format('Y-m-d H:i:s T')); switch ($unit) { case 'Y': case 'M': case 'D': $dt = "P{$interval}{$unit}"; break; case 'I': $dt = "PT{$interval}M"; break; case 'H': case 'S': $dt = "PT{$interval}{$unit}"; break; } $dv = new \DateInterval($dt); $triggerOn->add($dv); if ($triggerOn > $now) { $logger->debug('CAMPAIGN: Date to execute (' . $triggerOn->format('Y-m-d H:i:s T') . ') is later than now (' . $now->format('Y-m-d H:i:s T') . ')' . ($action['decisionPath'] == 'no' ? ' so ignore' : ' so schedule')); // Save some RAM for batch processing unset($now, $action, $dv, $dt); //the event is to be scheduled based on the time interval return $triggerOn; } } elseif ($action['triggerMode'] == 'date') { if (!$action['triggerDate'] instanceof \DateTime) { $triggerDate = new DateTimeHelper($action['triggerDate']); $action['triggerDate'] = $triggerDate->getDateTime(); unset($triggerDate); } $logger->debug('CAMPAIGN: Date execution on ' . $action['triggerDate']->format('Y-m-d H:i:s T')); $pastDue = $now >= $action['triggerDate']; if ($negate) { $logger->debug('CAMPAIGN: Negative comparison; Date to execute (' . $action['triggerDate']->format('Y-m-d H:i:s T') . ') compared to now (' . $now->format('Y-m-d H:i:s T') . ') and is thus ' . ($pastDue ? 'overdue' : 'not past due')); //it is past the scheduled trigger date and the lead has done nothing so return true to trigger //the event otherwise false to do nothing $return = $pastDue ? true : $action['triggerDate']; // Save some RAM for batch processing unset($now, $action); return $return; } elseif (!$pastDue) { $logger->debug('CAMPAIGN: Non-negative comparison; Date to execute (' . $action['triggerDate']->format('Y-m-d H:i:s T') . ') compared to now (' . $now->format('Y-m-d H:i:s T') . ') and is thus not past due'); //schedule the event return $action['triggerDate']; } } } $logger->debug('CAMPAIGN: Nothing stopped execution based on timing.'); //default is to trigger the event return true; }
/** * Fetch Lead stats for some period of time. * * @param integer $quantity of units * @param string $unit of time php.net/manual/en/class.dateinterval.php#dateinterval.props * @param array $options * * @return mixed * @throws \Doctrine\ORM\NoResultException * @throws \Doctrine\ORM\NonUniqueResultException */ public function getLeadStats($quantity, $unit, $options = array()) { $graphData = GraphHelper::prepareDatetimeLineGraphData($quantity, $unit, array('viewed')); // Load points for selected period $q = $this->getEntityManager()->getConnection()->createQueryBuilder(); $q->select(sprintf('count(*) as count, DATE(cl.date_added) as date_added'))->from(MAUTIC_TABLE_PREFIX . 'campaign_leads', 'cl'); $utc = new \DateTimeZone('UTC'); $graphData['fromDate']->setTimezone($utc); $q->andwhere($q->expr()->andX($q->expr()->gte('cl.date_added', ':date'), $q->expr()->eq('cl.manually_removed', ':false')))->setParameter('date', $graphData['fromDate']->format('Y-m-d H:i:s'))->setParameter('false', false, 'boolean')->groupBy('DATE(cl.date_added)')->orderBy('date_added', 'ASC'); if (isset($options['campaign_id'])) { $q->andwhere($q->expr()->gte('cl.campaign_id', (int) $options['campaign_id'])); } $datesAdded = $q->execute()->fetchAll(); $format = GraphHelper::getDateLabelFormat($unit); $formattedDates = array(); $dt = new DateTimeHelper(); foreach ($datesAdded as &$date) { $dt->setDateTime($date['date_added'], 'Y-m-d', 'utc'); $key = $dt->getDateTime()->format($format); $formattedDates[$key] = (int) $date['count']; } foreach ($graphData['labels'] as $key => $label) { $graphData['datasets'][0]['data'][$key] = isset($formattedDates[$label]) ? $formattedDates[$label] : 0; } unset($graphData['fromDate']); return $graphData; }
/** * {@inheritdoc} * * @param Email $entity * @param $unlock * * @return mixed */ public function saveEntity($entity, $unlock = true) { $now = new DateTimeHelper(); $type = $entity->getEmailType(); if (empty($type)) { // Just in case JS failed $entity->setEmailType('template'); } // Ensure that list emails are published if ($entity->getEmailType() == 'list') { $entity->setIsPublished(true); $entity->setPublishDown(null); $entity->setPublishUp(null); } //set the author for new pages if (!$entity->isNew()) { //increase the revision $revision = $entity->getRevision(); $revision++; $entity->setRevision($revision); } // Ensure links in template content don't have encoded ampersands if ($entity->getTemplate()) { $content = $entity->getContent(); foreach ($content as $key => $value) { $content[$key] = $this->cleanUrlsInContent($value); } $entity->setContent($content); } else { // Ensure links in HTML don't have encoded ampersands $htmlContent = $this->cleanUrlsInContent($entity->getCustomHtml()); $entity->setCustomHtml($htmlContent); } // Ensure links in PLAIN TEXT don't have encoded ampersands $plainContent = $this->cleanUrlsInContent($entity->getPlainText()); $entity->setPlainText($plainContent); // Reset the variant hit and start date if there are any changes and if this is an A/B test // Do it here in addition to the blanket resetVariants call so that it's available to the event listeners $changes = $entity->getChanges(); $parent = $entity->getVariantParent(); if ($parent !== null && !empty($changes) && empty($this->inConversion)) { $entity->setVariantSentCount(0); $entity->setVariantReadCount(0); $entity->setVariantStartDate($now->getDateTime()); } parent::saveEntity($entity, $unlock); // If parent, add this entity as a child of the parent so that it populates the list in the tab (due to Doctrine hanging on to entities in memory) if ($parent) { $parent->addVariantChild($entity); } // Reset associated variants if applicable due to changes if ($entity->isVariant() && !empty($changes) && empty($this->inConversion)) { $dateString = $now->toUtcString(); $parentId = !empty($parent) ? $parent->getId() : $entity->getId(); $this->getRepository()->resetVariants($parentId, $dateString); //if the parent was changed, then that parent/children must also be reset if (isset($changes['variantParent'])) { $this->getRepository()->resetVariants($changes['variantParent'][0], $dateString); } } }
/** * Note last ID read for user in channel * * @param User $userId * @param Channel $channelId * @param $lastId */ public function markRead(User $user, Channel $channel, $lastId) { $qb = $this->_em->createQueryBuilder(); $qb->select('s')->from('MauticChatBundle:ChannelStat', 's')->where($qb->expr()->eq('s.channel', ':channel'))->setParameter('channel', $channel)->andWhere($qb->expr()->eq('s.user', ':user'))->setParameter('user', $user); $exists = $qb->getQuery()->getResult(); $now = new DateTimeHelper(); if (!empty($exists)) { //update the record $exists[0]->setLastRead($lastId); $exists[0]->setDateRead($now->getDateTime()); $this->_em->persist($exists[0]); } else { $stat = new ChannelStat(); $stat->setChannel($channel); $stat->setUser($user); $stat->setLastRead($lastId); $stat->setDateRead($now->getDateTime()); $stat->setDateJoined($now->getDateTime()); $this->_em->persist($stat); } $this->_em->flush(); }
/** * Subscribe user to channel * * @param Channel $channel * @param User $user */ public function subscribeToChannel(Channel $channel, User $user = null) { if ($user == null) { $user = $this->factory->getUser(); } $stat = $this->getUserChannelStats($channel); if (empty($stat)) { $now = new DateTimeHelper(); //get the last read id $lastId = $this->getRepository()->getLastChatId($channel->getId()); $stat = new ChannelStat(); $stat->setChannel($channel); $stat->setUser($user); $stat->setLastRead($lastId); $stat->setDateRead($now->getDateTime()); $stat->setDateJoined($now->getDateTime()); $this->getRepository()->saveEntity($stat); } }
/** * {@inheritdoc} * * @param Page $entity * @param bool $unlock */ public function saveEntity($entity, $unlock = true) { if (empty($this->inConversion)) { $alias = $entity->getAlias(); if (empty($alias)) { $alias = $entity->getTitle(); } $alias = $this->cleanAlias($alias, '', false, '-'); //make sure alias is not already taken $repo = $this->getRepository(); $testAlias = $alias; $count = $repo->checkUniqueAlias($testAlias, $entity); $aliasTag = $count; while ($count) { $testAlias = $alias . $aliasTag; $count = $repo->checkUniqueAlias($testAlias, $entity); $aliasTag++; } if ($testAlias != $alias) { $alias = $testAlias; } $entity->setAlias($alias); } $now = new DateTimeHelper(); //set the author for new pages $isNew = $entity->isNew(); if (!$isNew) { //increase the revision $revision = $entity->getRevision(); $revision++; $entity->setRevision($revision); } // Reset the variant hit and start date if there are any changes and if this is an A/B test // Do it here in addition to the blanket resetVariants call so that it's available to the event listeners $changes = $entity->getChanges(); $parent = $entity->getVariantParent(); if ($parent !== null && !empty($changes) && empty($this->inConversion)) { $entity->setVariantHits(0); $entity->setVariantStartDate($now->getDateTime()); } parent::saveEntity($entity, $unlock); // If parent, add this entity as a child of the parent so that it populates the list in the tab (due to Doctrine hanging on to entities in memory) if ($parent) { $parent->addVariantChild($entity); } if ($translationParent = $entity->getTranslationParent()) { $translationParent->addTranslationChild($entity); } // Reset associated variants if applicable due to changes if ($entity->isVariant() && !empty($changes) && empty($this->inConversion)) { $dateString = $now->toUtcString(); $parentId = !empty($parent) ? $parent->getId() : $entity->getId(); $this->getRepository()->resetVariants($parentId, $dateString); //if the parent was changed, then that parent/children must also be reset if (isset($changes['variantParent'])) { $this->getRepository()->resetVariants($changes['variantParent'][0], $dateString); } } }