Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,844 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\IpAddress;
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\LeadDevice;
|
||||
|
||||
class Hit
|
||||
{
|
||||
public const TABLE_NAME = 'page_hits';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateHit;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateLeft;
|
||||
|
||||
private ?Page $page = null;
|
||||
|
||||
/**
|
||||
* @var Redirect|null
|
||||
*/
|
||||
private $redirect;
|
||||
|
||||
/**
|
||||
* @var Email|null
|
||||
*/
|
||||
private $email;
|
||||
|
||||
/**
|
||||
* @var Lead|null
|
||||
*/
|
||||
private $lead;
|
||||
|
||||
/**
|
||||
* @var IpAddress|null
|
||||
*/
|
||||
private $ipAddress;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $region;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $city;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $isp;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $organization;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $code;
|
||||
|
||||
private $referer;
|
||||
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $urlTitle;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $userAgent;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $remoteHost;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $pageLanguage;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private $browserLanguages = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
**/
|
||||
private $trackingId;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $source;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $sourceId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $query = [];
|
||||
|
||||
/**
|
||||
* @var LeadDevice|null
|
||||
*/
|
||||
private $device;
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable(self::TABLE_NAME)
|
||||
->setCustomRepositoryClass(HitRepository::class)
|
||||
->addIndex(['tracking_id'], 'page_hit_tracking_search')
|
||||
->addIndex(['code'], 'page_hit_code_search')
|
||||
->addIndex(['source', 'source_id'], 'page_hit_source_search')
|
||||
->addIndex(['date_hit', 'date_left'], 'date_hit_left_index')
|
||||
->addIndexWithOptions(['url'], 'page_hit_url', ['lengths' => [0 => 128]]);
|
||||
|
||||
$builder->addBigIntIdField();
|
||||
|
||||
$builder->createField('dateHit', 'datetime')
|
||||
->columnName('date_hit')
|
||||
->build();
|
||||
|
||||
$builder->createField('dateLeft', 'datetime')
|
||||
->columnName('date_left')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createManyToOne('page', 'Page')
|
||||
->addJoinColumn('page_id', 'id', true, false, 'SET NULL')
|
||||
->build();
|
||||
|
||||
$builder->createManyToOne('redirect', 'Redirect')
|
||||
->addJoinColumn('redirect_id', 'id', true, false, 'SET NULL')
|
||||
->build();
|
||||
|
||||
$builder->createManyToOne('email', Email::class)
|
||||
->addJoinColumn('email_id', 'id', true, false, 'SET NULL')
|
||||
->build();
|
||||
|
||||
$builder->addLead(true, 'SET NULL');
|
||||
|
||||
$builder->addIpAddress(true);
|
||||
|
||||
$builder->createField('country', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('region', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('city', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('isp', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('organization', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addField('code', 'integer');
|
||||
|
||||
$builder->createField('referer', 'text')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('url', 'text')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('urlTitle', 'string')
|
||||
->columnName('url_title')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('userAgent', 'text')
|
||||
->columnName('user_agent')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('remoteHost', 'string')
|
||||
->columnName('remote_host')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('pageLanguage', 'string')
|
||||
->columnName('page_language')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('browserLanguages', 'array')
|
||||
->columnName('browser_languages')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('trackingId', 'string')
|
||||
->columnName('tracking_id')
|
||||
->build();
|
||||
|
||||
$builder->createField('source', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('sourceId', 'integer')
|
||||
->columnName('source_id')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('query', 'array');
|
||||
|
||||
$builder->createManyToOne('device', LeadDevice::class)
|
||||
->addJoinColumn('device_id', 'id', true, false, 'SET NULL')
|
||||
->cascadePersist()
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('hit')
|
||||
->addProperties(
|
||||
[
|
||||
'id',
|
||||
'dateHit',
|
||||
'dateLeft',
|
||||
'page',
|
||||
'redirect',
|
||||
'email',
|
||||
'lead',
|
||||
'ipAddress',
|
||||
'country',
|
||||
'region',
|
||||
'city',
|
||||
'isp',
|
||||
'organization',
|
||||
'code',
|
||||
'referer',
|
||||
'url',
|
||||
'urlTitle',
|
||||
'userAgent',
|
||||
'remoteHost',
|
||||
'pageLanguage',
|
||||
'browserLanguages',
|
||||
'trackingId',
|
||||
'source',
|
||||
'sourceId',
|
||||
'query',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dateHit.
|
||||
*
|
||||
* @param \DateTime $dateHit
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setDateHit($dateHit)
|
||||
{
|
||||
$this->dateHit = $dateHit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dateHit.
|
||||
*
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateHit()
|
||||
{
|
||||
return $this->dateHit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateLeft()
|
||||
{
|
||||
return $this->dateLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dateLeft
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setDateLeft($dateLeft)
|
||||
{
|
||||
$this->dateLeft = $dateLeft;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set country.
|
||||
*
|
||||
* @param string $country
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setCountry($country)
|
||||
{
|
||||
$this->country = $country;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get country.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCountry()
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set region.
|
||||
*
|
||||
* @param string $region
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setRegion($region)
|
||||
{
|
||||
$this->region = $region;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get region.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRegion()
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set city.
|
||||
*
|
||||
* @param string $city
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setCity($city)
|
||||
{
|
||||
$this->city = $city;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get city.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCity()
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set isp.
|
||||
*
|
||||
* @param string $isp
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setIsp($isp)
|
||||
{
|
||||
$this->isp = $isp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get isp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIsp()
|
||||
{
|
||||
return $this->isp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set organization.
|
||||
*
|
||||
* @param string $organization
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setOrganization($organization)
|
||||
{
|
||||
$this->organization = $organization;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get organization.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrganization()
|
||||
{
|
||||
return $this->organization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set code.
|
||||
*
|
||||
* @param int $code
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set referer.
|
||||
*
|
||||
* @param string $referer
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setReferer($referer)
|
||||
{
|
||||
$this->referer = $referer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get referer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReferer()
|
||||
{
|
||||
return $this->referer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url title.
|
||||
*
|
||||
* @param string $urlTitle
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setUrlTitle($urlTitle)
|
||||
{
|
||||
$urlTitle = mb_strlen($urlTitle) <= 191 ? $urlTitle : mb_substr($urlTitle, 0, 191);
|
||||
$this->urlTitle = $urlTitle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlTitle()
|
||||
{
|
||||
return $this->urlTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set userAgent.
|
||||
*
|
||||
* @param string $userAgent
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setUserAgent($userAgent)
|
||||
{
|
||||
$this->userAgent = $userAgent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get userAgent.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAgent()
|
||||
{
|
||||
return $this->userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set remoteHost.
|
||||
*
|
||||
* @param string $remoteHost
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setRemoteHost($remoteHost)
|
||||
{
|
||||
$this->remoteHost = $remoteHost;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remoteHost.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteHost()
|
||||
{
|
||||
return $this->remoteHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set page.
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setPage(?Page $page = null)
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?Page
|
||||
*/
|
||||
public function getPage()
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Hit
|
||||
*/
|
||||
public function setIpAddress(IpAddress $ipAddress)
|
||||
{
|
||||
$this->ipAddress = $ipAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IpAddress|null
|
||||
*/
|
||||
public function getIpAddress()
|
||||
{
|
||||
return $this->ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $trackingId
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setTrackingId($trackingId)
|
||||
{
|
||||
$this->trackingId = $trackingId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trackingId.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTrackingId()
|
||||
{
|
||||
return $this->trackingId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pageLanguage.
|
||||
*
|
||||
* @param string $pageLanguage
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setPageLanguage($pageLanguage)
|
||||
{
|
||||
$this->pageLanguage = $pageLanguage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pageLanguage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPageLanguage()
|
||||
{
|
||||
return $this->pageLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set browserLanguages.
|
||||
*
|
||||
* @param array<string> $browserLanguages
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setBrowserLanguages($browserLanguages)
|
||||
{
|
||||
$this->browserLanguages = $browserLanguages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get browserLanguages.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getBrowserLanguages()
|
||||
{
|
||||
return $this->browserLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Lead
|
||||
*/
|
||||
public function getLead()
|
||||
{
|
||||
return $this->lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Hit
|
||||
*/
|
||||
public function setLead(Lead $lead)
|
||||
{
|
||||
$this->lead = $lead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setSource($source)
|
||||
{
|
||||
$this->source = $source;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSourceId()
|
||||
{
|
||||
return $this->sourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sourceId
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setSourceId($sourceId)
|
||||
{
|
||||
$this->sourceId = (int) $sourceId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getRedirect()
|
||||
{
|
||||
return $this->redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Hit
|
||||
*/
|
||||
public function setRedirect(Redirect $redirect)
|
||||
{
|
||||
$this->redirect = $redirect;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $email
|
||||
*/
|
||||
public function setEmail(Email $email): void
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
*
|
||||
* @return Hit
|
||||
*/
|
||||
public function setQuery($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LeadDevice
|
||||
*/
|
||||
public function getDeviceStat()
|
||||
{
|
||||
return $this->device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Hit
|
||||
*/
|
||||
public function setDeviceStat(LeadDevice $device)
|
||||
{
|
||||
$this->device = $device;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Doctrine\DBAL\Query\Expression\CompositeExpression;
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\TimelineTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Hit>
|
||||
*/
|
||||
class HitRepository extends CommonRepository
|
||||
{
|
||||
use TimelineTrait;
|
||||
|
||||
/**
|
||||
* Determine if the page hit is a unique.
|
||||
*
|
||||
* @param Page|Redirect $page
|
||||
* @param string $trackingId
|
||||
*/
|
||||
public function isUniquePageHit($page, $trackingId, ?Lead $lead = null): bool
|
||||
{
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$q2 = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$q2->select('null')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits', 'h');
|
||||
|
||||
// If we know the lead, use that to determine uniqueness
|
||||
if (null !== $lead && $lead->getId()) {
|
||||
$expr = CompositeExpression::and($q2->expr()->eq('h.lead_id', $lead->getId()));
|
||||
} else {
|
||||
$expr = CompositeExpression::and($q2->expr()->eq('h.tracking_id', ':id'));
|
||||
$q->setParameter('id', $trackingId);
|
||||
}
|
||||
|
||||
if ($page instanceof Page) {
|
||||
$expr = $expr->with(
|
||||
$q2->expr()->eq('h.page_id', $page->getId())
|
||||
);
|
||||
} elseif ($page instanceof Redirect) {
|
||||
$expr = $expr->with(
|
||||
$q2->expr()->eq('h.redirect_id', $page->getId())
|
||||
);
|
||||
}
|
||||
|
||||
$q2->where($expr);
|
||||
|
||||
$q->select('u.is_unique')
|
||||
->from(sprintf('(SELECT (NOT EXISTS (%s)) is_unique)', $q2->getSQL()), 'u');
|
||||
|
||||
return (bool) $q->executeQuery()->fetchOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lead's page hits.
|
||||
*
|
||||
* @param int|null $leadId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLeadHits($leadId = null, array $options = [])
|
||||
{
|
||||
$query = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$query->select('h.id as hitId, h.page_id, h.user_agent as userAgent, h.date_hit as dateHit, h.date_left as dateLeft, h.referer, h.source, h.source_id as sourceId, h.url, h.url_title as urlTitle, h.query, ds.client_info as clientInfo, ds.device, ds.device_os_name as deviceOsName, ds.device_brand as deviceBrand, ds.device_model as deviceModel, h.lead_id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits', 'h')
|
||||
->leftJoin('h', MAUTIC_TABLE_PREFIX.'pages', 'p', 'h.page_id = p.id');
|
||||
|
||||
if ($leadId) {
|
||||
$query->where('h.lead_id = :leadId')
|
||||
->setParameter('leadId', $leadId);
|
||||
}
|
||||
|
||||
if (isset($options['search']) && $options['search']) {
|
||||
$query->andWhere(
|
||||
$query->expr()->like('p.title', ':search')
|
||||
)->setParameter('search', '%'.$options['search'].'%');
|
||||
}
|
||||
|
||||
$query->leftjoin('h', MAUTIC_TABLE_PREFIX.'lead_devices', 'ds', 'ds.id = h.device_id');
|
||||
|
||||
if (isset($options['url']) && $options['url']) {
|
||||
$query->andWhere($query->expr()->eq('h.url', $query->expr()->literal($options['url'])));
|
||||
}
|
||||
|
||||
return $this->getTimelineResults($query, $options, 'p.title', 'h.date_hit', ['query'], ['dateHit', 'dateLeft'], null, 'h.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHitCountForSource($source, $sourceId = null, $fromDate = null, $code = 200)
|
||||
{
|
||||
$query = $this->createQueryBuilder('h');
|
||||
$query->select('count(distinct(h.trackingId)) as hitCount');
|
||||
$query->andWhere($query->expr()->eq('h.source', $query->expr()->literal($source)));
|
||||
|
||||
if (null != $sourceId) {
|
||||
if (is_array($sourceId)) {
|
||||
$query->andWhere($query->expr()->in('h.sourceId', ':sourceIds'))
|
||||
->setParameter('sourceIds', $sourceId);
|
||||
} else {
|
||||
$query->andWhere('h.sourceId = :sourceId')
|
||||
->setParameter('sourceId', $sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (null != $fromDate) {
|
||||
$query->andwhere($query->expr()->gte('h.dateHit', ':date'))
|
||||
->setParameter('date', $fromDate);
|
||||
}
|
||||
|
||||
$query->andWhere('h.code = :code')
|
||||
->setParameter('code', $code);
|
||||
|
||||
return $query->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of hits via an email clickthrough.
|
||||
*
|
||||
* @param int $code
|
||||
*/
|
||||
public function getEmailClickthroughHitCount($emailIds, ?\DateTime $fromDate = null, $code = 200): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
|
||||
if (!is_array($emailIds)) {
|
||||
$emailIds = [$emailIds];
|
||||
}
|
||||
|
||||
$q->select('count(distinct(h.tracking_id)) as hit_count, h.email_id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits', 'h')
|
||||
->where($q->expr()->in('h.email_id', $emailIds))
|
||||
->groupBy('h.email_id');
|
||||
|
||||
if (null != $fromDate) {
|
||||
$dateHelper = new DateTimeHelper($fromDate);
|
||||
$q->andwhere($q->expr()->gte('h.date_hit', ':date'))
|
||||
->setParameter('date', $dateHelper->toUtcString());
|
||||
}
|
||||
|
||||
$q->andWhere($q->expr()->eq('h.code', (int) $code));
|
||||
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
$hits = [];
|
||||
foreach ($results as $r) {
|
||||
$hits[$r['email_id']] = $r['hit_count'];
|
||||
}
|
||||
|
||||
return $hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count returning IP addresses.
|
||||
*/
|
||||
public function countReturningIp(): int
|
||||
{
|
||||
$q = $this->createQueryBuilder('h');
|
||||
$q->select('COUNT(h.ipAddress) as returning')
|
||||
->groupBy('h.ipAddress')
|
||||
->having($q->expr()->gt('COUNT(h.ipAddress)', 1));
|
||||
$results = $q->getQuery()->getResult();
|
||||
|
||||
return count($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count email clickthrough.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countEmailClickthrough()
|
||||
{
|
||||
$q = $this->createQueryBuilder('h');
|
||||
$q->select('COUNT(h.email) as clicks');
|
||||
$results = $q->getQuery()->getSingleResult();
|
||||
|
||||
return $results['clicks'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count how many visitors hit some page in last X $seconds.
|
||||
*
|
||||
* @param int $seconds
|
||||
* @param bool $notLeft
|
||||
*/
|
||||
public function countVisitors($seconds = 60, $notLeft = false): int
|
||||
{
|
||||
$now = new \DateTime();
|
||||
$viewingTime = new \DateInterval('PT'.$seconds.'S');
|
||||
$now->sub($viewingTime);
|
||||
$query = $this->createQueryBuilder('h');
|
||||
|
||||
$query->select('count(h.code) as visitors');
|
||||
|
||||
if ($seconds) {
|
||||
$query->where($query->expr()->gte('h.dateHit', ':date'))
|
||||
->setParameter('date', $now);
|
||||
}
|
||||
|
||||
if ($notLeft) {
|
||||
$query->andWhere($query->expr()->isNull('h.dateLeft'));
|
||||
}
|
||||
|
||||
$result = $query->getQuery()->getSingleResult();
|
||||
|
||||
if (!isset($result['visitors'])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) $result['visitors'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest hit.
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function getLatestHit($options): ?\DateTime
|
||||
{
|
||||
$sq = $this->_em->getConnection()->createQueryBuilder();
|
||||
$sq->select('h.date_hit latest_hit')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits', 'h');
|
||||
|
||||
if (isset($options['leadId'])) {
|
||||
$sq->andWhere(
|
||||
$sq->expr()->eq('h.lead_id', $options['leadId'])
|
||||
);
|
||||
}
|
||||
if (isset($options['urls']) && $options['urls']) {
|
||||
$inUrls = (!is_array($options['urls'])) ? [$options['urls']] : $options['urls'];
|
||||
foreach ($inUrls as $k => $u) {
|
||||
$sq->andWhere($sq->expr()->like('h.url', ':url_'.$k))
|
||||
->setParameter('url_'.$k, $u);
|
||||
}
|
||||
}
|
||||
if (isset($options['second_to_last'])) {
|
||||
$sq->andWhere($sq->expr()->neq('h.id', $options['second_to_last']));
|
||||
} else {
|
||||
$sq->orderBy('h.date_hit', 'DESC limit 1');
|
||||
}
|
||||
$result = $sq->executeQuery()->fetchAssociative();
|
||||
|
||||
return $result ? new \DateTime($result['latest_hit'], new \DateTimeZone('UTC')) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bounces.
|
||||
*
|
||||
* @param array|string $pageIds
|
||||
* @param bool $isVariantCheck
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getBounces($pageIds, ?\DateTime $fromDate = null, $isVariantCheck = false): array
|
||||
{
|
||||
$inOrEq = (!is_array($pageIds)) ? 'eq' : 'in';
|
||||
|
||||
$hitsColumn = ($isVariantCheck) ? 'variant_hits' : 'unique_hits';
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$pages = $q->select("p.id, p.$hitsColumn as totalHits, p.title")
|
||||
->from(MAUTIC_TABLE_PREFIX.'pages', 'p')
|
||||
->where($q->expr()->$inOrEq('p.id', $pageIds))
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
$return = [];
|
||||
foreach ($pages as $p) {
|
||||
$return[$p['id']] = [
|
||||
'totalHits' => (int) $p['totalHits'],
|
||||
'bounces' => 0,
|
||||
'rate' => 0,
|
||||
'title' => $p['title'],
|
||||
];
|
||||
}
|
||||
|
||||
// Get the total number of bounces - simplified query for if date_left is null, it'll more than likely be a bounce or
|
||||
// else we would have recorded the date_left on a subsequent page hit
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$expr = $q->expr()->and(
|
||||
$q->expr()->$inOrEq('h.page_id', $pageIds),
|
||||
$q->expr()->eq('h.code', 200),
|
||||
$q->expr()->isNull('h.date_left')
|
||||
);
|
||||
|
||||
if (null !== $fromDate) {
|
||||
// make sure the date is UTC
|
||||
$dt = new DateTimeHelper($fromDate, 'Y-m-d H:i:s', 'local');
|
||||
$expr = $expr->with(
|
||||
$q->expr()->gte('h.date_hit', $q->expr()->literal($dt->toUtcString()))
|
||||
);
|
||||
}
|
||||
|
||||
$q->select('count(*) as bounces, h.page_id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits', 'h')
|
||||
->where($expr)
|
||||
->groupBy('h.page_id');
|
||||
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
foreach ($results as $p) {
|
||||
$return[$p['page_id']]['bounces'] = (int) $p['bounces'];
|
||||
$return[$p['page_id']]['rate'] = ($return[$p['page_id']]['totalHits']) ? round(
|
||||
($p['bounces'] / $return[$p['page_id']]['totalHits']) * 100,
|
||||
2
|
||||
) : 0;
|
||||
}
|
||||
|
||||
return (!is_array($pageIds)) ? $return[$pageIds] : $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of dwell time labels with ranges.
|
||||
*/
|
||||
public function getDwellTimeLabels(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'label' => '< 1m',
|
||||
'from' => 0,
|
||||
'till' => 60,
|
||||
],
|
||||
[
|
||||
'label' => '1 - 5m',
|
||||
'from' => 60,
|
||||
'till' => 300,
|
||||
],
|
||||
[
|
||||
'label' => '5 - 10m',
|
||||
'value' => 0,
|
||||
'from' => 300,
|
||||
'till' => 600,
|
||||
],
|
||||
[
|
||||
'label' => '> 10m',
|
||||
'from' => 600,
|
||||
'till' => 999999,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dwell times for bunch of pages.
|
||||
*/
|
||||
public function getDwellTimesForPages(array $pageIds, array $options): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->from(MAUTIC_TABLE_PREFIX.'page_hits', 'ph')
|
||||
->leftJoin('ph', MAUTIC_TABLE_PREFIX.'pages', 'p', 'ph.page_id = p.id')
|
||||
->select('ph.page_id, ph.date_hit, ph.date_left, p.title')
|
||||
->orderBy('ph.date_hit', 'ASC')
|
||||
->andWhere(
|
||||
$q->expr()->and(
|
||||
$q->expr()->in('ph.page_id', $pageIds)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($options['fromDate'])) {
|
||||
// make sure the date is UTC
|
||||
$dt = new DateTimeHelper($options['fromDate']);
|
||||
$q->andWhere(
|
||||
$q->expr()->gte('ph.date_hit', $q->expr()->literal($dt->toUtcString()))
|
||||
);
|
||||
}
|
||||
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
// loop to structure
|
||||
$times = [];
|
||||
$titles = [];
|
||||
|
||||
foreach ($results as $r) {
|
||||
$dateHit = $r['date_hit'] ? new \DateTime($r['date_hit']) : 0;
|
||||
$dateLeft = $r['date_left'] ? new \DateTime($r['date_left']) : 0;
|
||||
|
||||
$titles[$r['page_id']] = $r['title'];
|
||||
$times[$r['page_id']][] = $dateLeft ? ($dateLeft->getTimestamp() - $dateHit->getTimestamp()) : 0;
|
||||
}
|
||||
|
||||
// now loop to create stats
|
||||
$stats = [];
|
||||
|
||||
foreach ($times as $pid => $time) {
|
||||
$stats[$pid] = $this->countStats($time);
|
||||
$stats[$pid]['title'] = $titles[$pid];
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dwell times for bunch of URLs.
|
||||
*
|
||||
* @param string $url
|
||||
*/
|
||||
public function getDwellTimesForUrl($url, array $options): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->from(MAUTIC_TABLE_PREFIX.'page_hits', 'ph')
|
||||
->leftJoin('ph', MAUTIC_TABLE_PREFIX.'pages', 'p', 'ph.page_id = p.id')
|
||||
->select('ph.id, ph.page_id, ph.date_hit, ph.date_left, ph.tracking_id, ph.page_language, p.title')
|
||||
->orderBy('ph.date_hit', 'ASC')
|
||||
->andWhere($q->expr()->like('ph.url', ':url'))
|
||||
->setParameter('url', $url);
|
||||
|
||||
if (isset($options['leadId']) && $options['leadId']) {
|
||||
$q->andWhere(
|
||||
$q->expr()->eq('ph.lead_id', (int) $options['leadId'])
|
||||
);
|
||||
}
|
||||
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
$times = [];
|
||||
|
||||
foreach ($results as $r) {
|
||||
$dateHit = $r['date_hit'] ? new \DateTime($r['date_hit']) : 0;
|
||||
$dateLeft = $r['date_left'] ? new \DateTime($r['date_left']) : 0;
|
||||
$times[] = $dateLeft ? ($dateLeft->getTimestamp() - $dateHit->getTimestamp()) : 0;
|
||||
}
|
||||
|
||||
return $this->countStats($times);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count stats from hit times.
|
||||
*
|
||||
* @param array $times
|
||||
*/
|
||||
public function countStats($times): array
|
||||
{
|
||||
return [
|
||||
'sum' => array_sum($times),
|
||||
'min' => count($times) ? min($times) : 0,
|
||||
'max' => count($times) ? max($times) : 0,
|
||||
'average' => count($times) ? round(array_sum($times) / count($times)) : 0,
|
||||
'count' => count($times),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a hit with the the time the user left.
|
||||
*
|
||||
* @param int $lastHitId
|
||||
*/
|
||||
public function updateHitDateLeft($lastHitId): void
|
||||
{
|
||||
$dt = new DateTimeHelper();
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'page_hits')
|
||||
->set('date_left', ':datetime')
|
||||
->where('id = '.(int) $lastHitId)
|
||||
->setParameter('datetime', $dt->toUtcString());
|
||||
$q->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of referers ordered by it's count.
|
||||
*
|
||||
* @param \Doctrine\DBAL\Query\QueryBuilder $query
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getReferers($query, $limit = 10, $offset = 0): array
|
||||
{
|
||||
$query->select('ph.referer, count(ph.referer) as sessions')
|
||||
->groupBy('ph.referer')
|
||||
->orderBy('sessions', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $query->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of referers ordered by it's count.
|
||||
*
|
||||
* @param \Doctrine\DBAL\Query\QueryBuilder $query
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @param string $column
|
||||
* @param string $as
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getMostVisited($query, $limit = 10, $offset = 0, $column = 'p.hits', $as = ''): array
|
||||
{
|
||||
if ($as) {
|
||||
$as = ' as "'.$as.'"';
|
||||
}
|
||||
|
||||
$query->select('p.title, p.id, '.$column.$as)
|
||||
->where('p.id IS NOT NULL')
|
||||
->groupBy('p.id, p.title, '.$column)
|
||||
->orderBy($column, 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $query->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
public function updateLeadByTrackingId($leadId, $newTrackingId, $oldTrackingId): void
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'page_hits')
|
||||
->set('lead_id', (int) $leadId)
|
||||
->set('tracking_id', ':newTrackingId')
|
||||
->where(
|
||||
$q->expr()->eq('tracking_id', ':oldTrackingId')
|
||||
)
|
||||
->setParameters([
|
||||
'newTrackingId' => $newTrackingId,
|
||||
'oldTrackingId' => $oldTrackingId,
|
||||
])
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates lead ID (e.g. after a lead merge).
|
||||
*/
|
||||
public function updateLead($fromLeadId, $toLeadId): void
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'page_hits')
|
||||
->set('lead_id', (int) $toLeadId)
|
||||
->where('lead_id = '.(int) $fromLeadId)
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
public function getLatestHitDateByLead(int $leadId, ?string $trackingId = null): ?\DateTime
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder()
|
||||
->select('MAX(date_hit)')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits')
|
||||
->where('lead_id = :leadId')
|
||||
->setParameter('leadId', $leadId);
|
||||
|
||||
if (null != $trackingId) {
|
||||
$q->andWhere('tracking_id = :trackingId')
|
||||
->setParameter('trackingId', $trackingId);
|
||||
}
|
||||
|
||||
$result = $q->executeQuery()->fetchOne();
|
||||
|
||||
return $result ? new \DateTime($result, new \DateTimeZone('UTC')) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,957 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use ApiPlatform\Metadata\Put;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CategoryBundle\Entity\Category;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\FormEntity;
|
||||
use Mautic\CoreBundle\Entity\TranslationEntityInterface;
|
||||
use Mautic\CoreBundle\Entity\TranslationEntityTrait;
|
||||
use Mautic\CoreBundle\Entity\UuidInterface;
|
||||
use Mautic\CoreBundle\Entity\UuidTrait;
|
||||
use Mautic\CoreBundle\Entity\VariantEntityInterface;
|
||||
use Mautic\CoreBundle\Entity\VariantEntityTrait;
|
||||
use Mautic\CoreBundle\Validator\EntityEvent;
|
||||
use Mautic\ProjectBundle\Entity\ProjectTrait;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(security: "is_granted('page:pages:viewown')"),
|
||||
new Post(security: "is_granted('page:pages:create')"),
|
||||
new Get(security: "is_granted('page:pages:viewown')"),
|
||||
new Put(security: "is_granted('page:pages:editown')"),
|
||||
new Patch(security: "is_granted('page:pages:editother')"),
|
||||
new Delete(security: "is_granted('page:pages:deleteown')"),
|
||||
],
|
||||
normalizationContext: [
|
||||
'groups' => ['page:read'],
|
||||
'swagger_definition_name' => 'Read',
|
||||
'api_included' => ['category', 'translationChildren'],
|
||||
],
|
||||
denormalizationContext: [
|
||||
'groups' => ['page:write'],
|
||||
'swagger_definition_name' => 'Write',
|
||||
]
|
||||
)]
|
||||
/**
|
||||
* @use TranslationEntityTrait<Page>
|
||||
* @use VariantEntityTrait<Page>
|
||||
*/
|
||||
class Page extends FormEntity implements TranslationEntityInterface, VariantEntityInterface, UuidInterface
|
||||
{
|
||||
use TranslationEntityTrait;
|
||||
use VariantEntityTrait;
|
||||
use UuidTrait;
|
||||
use ProjectTrait;
|
||||
public const ENTITY_NAME = 'page';
|
||||
|
||||
public const TABLE_NAME = 'pages';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['page:read', 'download:read', 'email:read'])]
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $alias;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $customHtml;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $content = [];
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $publishUp;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $publishDown;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $hits = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $uniqueHits = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $variantHits = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $revision = 1;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $metaDescription;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $headScript;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $footerScript;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $redirectType;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $redirectUrl;
|
||||
|
||||
/**
|
||||
* @var Category|null
|
||||
**/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $isPreferenceCenter;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private $noIndex;
|
||||
|
||||
/**
|
||||
* Used to identify the page for the builder.
|
||||
*/
|
||||
private $sessionId;
|
||||
|
||||
private ?PageDraft $draft = null;
|
||||
|
||||
private bool $isCloned = false;
|
||||
|
||||
private ?int $cloneObjectId = null;
|
||||
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private ?bool $publicPreview = true;
|
||||
|
||||
#[Groups(['page:read', 'page:write', 'download:read', 'email:read'])]
|
||||
private bool $isDuplicate = false;
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->cloneObjectId = (int) $this->id;
|
||||
$this->isCloned = true;
|
||||
$this->id = null;
|
||||
$this->clearTranslations();
|
||||
$this->clearVariants();
|
||||
$this->setDraft(null);
|
||||
|
||||
parent::__clone();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->translationChildren = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->variantChildren = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->initializeProjects();
|
||||
}
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable(self::TABLE_NAME)
|
||||
->setCustomRepositoryClass(PageRepository::class)
|
||||
->addIndex(['alias'], 'page_alias_search');
|
||||
|
||||
$builder->addId();
|
||||
|
||||
$builder->addField('title', 'string');
|
||||
|
||||
$builder->addField('alias', 'string');
|
||||
|
||||
$builder->addNullableField('template', 'string');
|
||||
|
||||
$builder->createField('customHtml', 'text')
|
||||
->columnName('custom_html')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('content', 'array')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addPublishDates();
|
||||
|
||||
$builder->addField('hits', 'integer');
|
||||
|
||||
$builder->createField('uniqueHits', 'integer')
|
||||
->columnName('unique_hits')
|
||||
->build();
|
||||
|
||||
$builder->createField('variantHits', 'integer')
|
||||
->columnName('variant_hits')
|
||||
->build();
|
||||
|
||||
$builder->addField('revision', 'integer');
|
||||
|
||||
$builder->createField('metaDescription', 'string')
|
||||
->columnName('meta_description')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('headScript', 'text')
|
||||
->columnName('head_script')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('footerScript', 'text')
|
||||
->columnName('footer_script')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('redirectType', 'string')
|
||||
->columnName('redirect_type')
|
||||
->nullable()
|
||||
->length(100)
|
||||
->build();
|
||||
|
||||
$builder->createField('redirectUrl', 'string')
|
||||
->columnName('redirect_url')
|
||||
->nullable()
|
||||
->length(2048)
|
||||
->build();
|
||||
|
||||
$builder->addCategory();
|
||||
|
||||
$builder->createField('isPreferenceCenter', 'boolean')
|
||||
->columnName('is_preference_center')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('noIndex', 'boolean')
|
||||
->columnName('no_index')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createOneToOne('draft', PageDraft::class)
|
||||
->mappedBy('page')
|
||||
->fetchExtraLazy()
|
||||
->cascadeAll()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('publicPreview', Types::BOOLEAN, 'public_preview');
|
||||
|
||||
self::addTranslationMetadata($builder, self::class);
|
||||
self::addVariantMetadata($builder, self::class);
|
||||
static::addUuidField($builder);
|
||||
self::addProjectsField($builder, 'page_projects_xref', 'page_id');
|
||||
}
|
||||
|
||||
public static function loadValidatorMetadata(ClassMetadata $metadata): void
|
||||
{
|
||||
$metadata->addPropertyConstraint('title', new NotBlank([
|
||||
'message' => 'mautic.core.title.required',
|
||||
]));
|
||||
|
||||
$metadata->addConstraint(new Callback(
|
||||
function (Page $page, ExecutionContextInterface $context): void {
|
||||
$type = $page->getRedirectType();
|
||||
if (!is_null($type)) {
|
||||
$validator = $context->getValidator();
|
||||
$violations = $validator->validate(
|
||||
$page->getRedirectUrl(),
|
||||
[
|
||||
new Assert\Url(),
|
||||
new NotBlank(['message' => 'mautic.core.value.required']),
|
||||
],
|
||||
);
|
||||
|
||||
foreach ($violations as $violation) {
|
||||
$context->buildViolation($violation->getMessage())
|
||||
->atPath('redirectUrl')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
if ($page->isVariant()) {
|
||||
// Get a summation of weights
|
||||
$parent = $page->getVariantParent();
|
||||
$children = $parent ? $parent->getVariantChildren() : $page->getVariantChildren();
|
||||
|
||||
$total = 0;
|
||||
foreach ($children as $child) {
|
||||
$settings = $child->getVariantSettings();
|
||||
$total += (int) $settings['weight'];
|
||||
}
|
||||
|
||||
if ($total > 100) {
|
||||
$context->buildViolation('mautic.core.variant_weights_invalid')
|
||||
->atPath('variantSettings[weight]')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
$metadata->addConstraint(new EntityEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('page')
|
||||
->addListProperties(
|
||||
[
|
||||
'id',
|
||||
'title',
|
||||
'alias',
|
||||
'category',
|
||||
]
|
||||
)
|
||||
->addProperties(
|
||||
[
|
||||
'language',
|
||||
'publishUp',
|
||||
'publishDown',
|
||||
'hits',
|
||||
'uniqueHits',
|
||||
'variantHits',
|
||||
'revision',
|
||||
'metaDescription',
|
||||
'redirectType',
|
||||
'redirectUrl',
|
||||
'isPreferenceCenter',
|
||||
'noIndex',
|
||||
'variantSettings',
|
||||
'variantStartDate',
|
||||
'variantParent',
|
||||
'variantChildren',
|
||||
'translationParent',
|
||||
'translationChildren',
|
||||
'template',
|
||||
'customHtml',
|
||||
]
|
||||
)
|
||||
->setMaxDepth(1, 'variantParent')
|
||||
->setMaxDepth(1, 'variantChildren')
|
||||
->setMaxDepth(1, 'translationParent')
|
||||
->setMaxDepth(1, 'translationChildren')
|
||||
->build();
|
||||
|
||||
self::addProjectsInLoadApiMetadata($metadata, 'page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title.
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->isChanged('title', $title);
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alias.
|
||||
*
|
||||
* @param string $alias
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setAlias($alias)
|
||||
{
|
||||
$this->isChanged('alias', $alias);
|
||||
$this->alias = $alias;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alias.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set content.
|
||||
*
|
||||
* @param array<string> $content
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->isChanged('content', $content);
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set publishUp.
|
||||
*
|
||||
* @param \DateTime $publishUp
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setPublishUp($publishUp)
|
||||
{
|
||||
$this->isChanged('publishUp', $publishUp);
|
||||
$this->publishUp = $publishUp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get publishUp.
|
||||
*
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishUp()
|
||||
{
|
||||
return $this->publishUp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set publishDown.
|
||||
*
|
||||
* @param \DateTime $publishDown
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setPublishDown($publishDown)
|
||||
{
|
||||
$this->isChanged('publishDown', $publishDown);
|
||||
$this->publishDown = $publishDown;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get publishDown.
|
||||
*
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishDown()
|
||||
{
|
||||
return $this->publishDown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hits.
|
||||
*
|
||||
* @param int $hits
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setHits($hits)
|
||||
{
|
||||
$this->hits = $hits;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hits.
|
||||
*
|
||||
* @param bool $includeVariants
|
||||
*
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function getHits($includeVariants = false)
|
||||
{
|
||||
return ($includeVariants) ? $this->getAccumulativeVariantCount('getHits') : $this->hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set revision.
|
||||
*
|
||||
* @param int $revision
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setRevision($revision)
|
||||
{
|
||||
$this->revision = $revision;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get revision.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRevision()
|
||||
{
|
||||
return $this->revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set metaDescription.
|
||||
*
|
||||
* @param string $metaDescription
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setMetaDescription($metaDescription)
|
||||
{
|
||||
$this->isChanged('metaDescription', $metaDescription);
|
||||
$this->metaDescription = $metaDescription;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metaDescription.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMetaDescription()
|
||||
{
|
||||
return $this->metaDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set headScript.
|
||||
*
|
||||
* @param string $headScript
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setHeadScript($headScript)
|
||||
{
|
||||
$this->headScript = $headScript;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get headScript.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeadScript()
|
||||
{
|
||||
return $this->headScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set footerScript.
|
||||
*
|
||||
* @param string $footerScript
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setFooterScript($footerScript)
|
||||
{
|
||||
$this->footerScript = $footerScript;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get footerScript.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFooterScript()
|
||||
{
|
||||
return $this->footerScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ?string $redirectType
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setRedirectType($redirectType)
|
||||
{
|
||||
$this->isChanged('redirectType', $redirectType);
|
||||
$this->redirectType = $redirectType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string
|
||||
*/
|
||||
public function getRedirectType()
|
||||
{
|
||||
return $this->redirectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set redirectUrl.
|
||||
*
|
||||
* @param string $redirectUrl
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setRedirectUrl($redirectUrl)
|
||||
{
|
||||
$this->isChanged('redirectUrl', $redirectUrl);
|
||||
$this->redirectUrl = $redirectUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get redirectUrl.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRedirectUrl()
|
||||
{
|
||||
return $this->redirectUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set category.
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setCategory(?Category $category = null)
|
||||
{
|
||||
$this->isChanged('category', $category);
|
||||
$this->category = $category;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category.
|
||||
*
|
||||
* @return Category
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $isPreferenceCenter
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setIsPreferenceCenter($isPreferenceCenter)
|
||||
{
|
||||
$sanitizedValue = null === $isPreferenceCenter ? null : (bool) $isPreferenceCenter;
|
||||
$this->isChanged('isPreferenceCenter', $sanitizedValue);
|
||||
$this->isPreferenceCenter = $sanitizedValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getIsPreferenceCenter()
|
||||
{
|
||||
return $this->isPreferenceCenter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $noIndex
|
||||
*/
|
||||
public function setNoIndex($noIndex): void
|
||||
{
|
||||
$sanitizedValue = null === $noIndex ? null : (bool) $noIndex;
|
||||
$this->isChanged('noIndex', $sanitizedValue);
|
||||
$this->noIndex = $sanitizedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getNoIndex()
|
||||
{
|
||||
return $this->noIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sessionId.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setSessionId($id)
|
||||
{
|
||||
$this->sessionId = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sessionId.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSessionId()
|
||||
{
|
||||
return $this->sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set template.
|
||||
*
|
||||
* @param string $template
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setTemplate($template)
|
||||
{
|
||||
$this->isChanged('template', $template);
|
||||
$this->template = $template;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplate()
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
protected function isChanged($prop, $val)
|
||||
{
|
||||
$getter = 'get'.ucfirst($prop);
|
||||
$current = $this->$getter();
|
||||
|
||||
if ('translationParent' == $prop || 'variantParent' == $prop || 'category' == $prop) {
|
||||
$currentId = ($current) ? $current->getId() : '';
|
||||
$newId = ($val) ? $val->getId() : null;
|
||||
if ($currentId != $newId) {
|
||||
$this->changes[$prop] = [$currentId, $newId];
|
||||
}
|
||||
} else {
|
||||
parent::isChanged($prop, $val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set uniqueHits.
|
||||
*
|
||||
* @param int $uniqueHits
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function setUniqueHits($uniqueHits)
|
||||
{
|
||||
$this->uniqueHits = $uniqueHits;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uniqueHits.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUniqueHits($includeVariants = false)
|
||||
{
|
||||
return ($includeVariants) ? $this->getAccumulativeVariantCount('getUniqueHits') : $this->uniqueHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includeVariants
|
||||
*
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function getVariantHits($includeVariants = false)
|
||||
{
|
||||
return ($includeVariants) ? $this->getAccumulativeVariantCount('getVariantHits') : $this->variantHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $variantHits
|
||||
*/
|
||||
public function setVariantHits($variantHits): void
|
||||
{
|
||||
$this->variantHits = $variantHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomHtml()
|
||||
{
|
||||
return $this->customHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $customHtml
|
||||
*/
|
||||
public function setCustomHtml($customHtml): void
|
||||
{
|
||||
$this->customHtml = $customHtml;
|
||||
}
|
||||
|
||||
public function hasDraft(): bool
|
||||
{
|
||||
return !is_null($this->getDraft());
|
||||
}
|
||||
|
||||
public function getDraftContent(): ?string
|
||||
{
|
||||
return $this->hasDraft() ? $this->getDraft()->getHtml() : null;
|
||||
}
|
||||
|
||||
public function getDraft(): ?PageDraft
|
||||
{
|
||||
return $this->draft;
|
||||
}
|
||||
|
||||
public function setDraft(?PageDraft $draft): void
|
||||
{
|
||||
$this->draft = $draft;
|
||||
}
|
||||
|
||||
public function getIsClone(): bool
|
||||
{
|
||||
return $this->isCloned;
|
||||
}
|
||||
|
||||
public function getCloneObjectId(): int
|
||||
{
|
||||
return $this->cloneObjectId;
|
||||
}
|
||||
|
||||
public function getPublicPreview(): bool
|
||||
{
|
||||
return $this->publicPreview;
|
||||
}
|
||||
|
||||
public function isPublicPreview(): bool
|
||||
{
|
||||
return $this->publicPreview;
|
||||
}
|
||||
|
||||
public function setPublicPreview(bool $publicPreview): self
|
||||
{
|
||||
$this->isChanged('publicPreview', $publicPreview);
|
||||
$this->publicPreview = $publicPreview;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDuplicate(): bool
|
||||
{
|
||||
return $this->isDuplicate;
|
||||
}
|
||||
|
||||
public function setIsDuplicate(bool $isDuplicate): void
|
||||
{
|
||||
$this->isDuplicate = $isDuplicate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
|
||||
class PageDraft
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const TABLE_NAME = 'pages_draft';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const REGEX_DECODE_AMPERSAND = '/((https?|ftps?):\/\/)([a-zA-Z0-9-\.{}]*[a-zA-Z0-9=}]*)(\??)([^\s\"\]]+)?/i';
|
||||
|
||||
private ?int $id = null;
|
||||
|
||||
public function __construct(
|
||||
private Page $page,
|
||||
private ?string $html = null,
|
||||
private ?string $template = null,
|
||||
private bool $publicPreview = true,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable(self::TABLE_NAME)
|
||||
->setCustomRepositoryClass(PageDraftRepository::class)
|
||||
->addLifecycleEvent('cleanUrlsInContent', Events::preUpdate)
|
||||
->addLifecycleEvent('cleanUrlsInContent', Events::prePersist);
|
||||
|
||||
$builder->addId();
|
||||
$builder->addNullableField('html', Types::TEXT);
|
||||
$builder->addNullableField('template', Types::STRING);
|
||||
$builder->createField('publicPreview', Types::BOOLEAN)
|
||||
->columnName('public_preview')
|
||||
->nullable(false)
|
||||
->option('default', 1)
|
||||
->build();
|
||||
|
||||
$builder->createOneToOne('page', Page::class)
|
||||
->inversedBy('draft')
|
||||
->addJoinColumn('page_id', 'id', false)
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle callback to clean URLs in the content.
|
||||
*/
|
||||
public function cleanUrlsInContent(): void
|
||||
{
|
||||
$this->html = $this->decodeAmpersands((string) $this->html);
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(?int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getPage(): Page
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
public function getHtml(): ?string
|
||||
{
|
||||
return $this->html;
|
||||
}
|
||||
|
||||
public function setPage(Page $page): void
|
||||
{
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
public function setHtml(?string $html): void
|
||||
{
|
||||
$this->html = $html;
|
||||
}
|
||||
|
||||
public function getTemplate(): ?string
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
public function setTemplate(?string $template): void
|
||||
{
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
public function isPublicPreview(): bool
|
||||
{
|
||||
return (bool) $this->publicPreview;
|
||||
}
|
||||
|
||||
public function setPublicPreview(bool $publicPreview): void
|
||||
{
|
||||
$this->publicPreview = $publicPreview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all links in content and decode &
|
||||
* This even works with double encoded ampersands.
|
||||
*/
|
||||
private function decodeAmpersands(string $content): string
|
||||
{
|
||||
if (!preg_match_all(self::REGEX_DECODE_AMPERSAND, $content, $matches)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ($matches[0] as $url) {
|
||||
$newUrl = $url;
|
||||
while (str_contains($newUrl, '&')) {
|
||||
$newUrl = str_replace('&', '&', $newUrl);
|
||||
}
|
||||
$content = str_replace($url, $newUrl, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
|
||||
class PageDraftRepository extends CommonRepository
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\ProjectBundle\Entity\ProjectRepositoryTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Page>
|
||||
*/
|
||||
class PageRepository extends CommonRepository
|
||||
{
|
||||
use ProjectRepositoryTrait;
|
||||
|
||||
public function getEntities(array $args = [])
|
||||
{
|
||||
$select = ['p'];
|
||||
|
||||
if (!empty($args['submissionCount'])) {
|
||||
// use a subquery to get a count of submissions otherwise doctrine will not pull all of the results
|
||||
$sq = $this->_em->createQueryBuilder()
|
||||
->select('count(fs.id)')
|
||||
->from(\Mautic\FormBundle\Entity\Submission::class, 'fs')
|
||||
->where('fs.page = p');
|
||||
|
||||
$select[] = '('.$sq->getDql().') as submission_count';
|
||||
}
|
||||
|
||||
$q = $this->createQueryBuilder('p')
|
||||
->select($select)
|
||||
->leftJoin('p.category', 'c');
|
||||
|
||||
$args['qb'] = $q;
|
||||
|
||||
return parent::getEntities($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $alias
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function checkPageUniqueAlias($alias, $ignoreIds = [])
|
||||
{
|
||||
$q = $this->createQueryBuilder('e')
|
||||
->select('count(e.id) as alias_count')
|
||||
->where('e.alias = :alias');
|
||||
$q->setParameter('alias', $alias);
|
||||
|
||||
if (!empty($ignoreIds)) {
|
||||
$q->andWhere(
|
||||
$q->expr()->notIn('e.id', ':ignoreIds')
|
||||
)
|
||||
->setParameter('ignoreIds', $ignoreIds);
|
||||
}
|
||||
|
||||
$results = $q->getQuery()->getSingleResult();
|
||||
|
||||
return $results['alias_count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @param int $limit
|
||||
* @param int $start
|
||||
* @param bool $viewOther
|
||||
* @param string|bool $topLevel
|
||||
* @param array $ignoreIds
|
||||
* @param array $extraColumns
|
||||
* @param bool $publishedOnly
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPageList($search = '', $limit = 10, $start = 0, $viewOther = false, $topLevel = false, $ignoreIds = [], $extraColumns = [], $publishedOnly = false)
|
||||
{
|
||||
$q = $this->createQueryBuilder('p');
|
||||
$q->select(sprintf('partial p.{id, title, language, alias %s}', empty($extraColumns) ? '' : ','.implode(',', $extraColumns)));
|
||||
|
||||
if (!empty($search)) {
|
||||
$q->andWhere($q->expr()->like('p.title', ':search'))
|
||||
->setParameter('search', "{$search}%");
|
||||
}
|
||||
|
||||
if (!$viewOther) {
|
||||
$q->andWhere($q->expr()->eq('p.createdBy', ':id'))
|
||||
->setParameter('id', $this->currentUser->getId());
|
||||
}
|
||||
|
||||
if ('translation' == $topLevel) {
|
||||
// only get top level pages
|
||||
$q->andWhere($q->expr()->isNull('p.translationParent'));
|
||||
} elseif ('variant' == $topLevel) {
|
||||
$q->andWhere($q->expr()->isNull('p.variantParent'));
|
||||
}
|
||||
|
||||
if (!empty($ignoreIds)) {
|
||||
$q->andWhere($q->expr()->notIn('p.id', ':pageIds'))
|
||||
->setParameter('pageIds', $ignoreIds);
|
||||
}
|
||||
|
||||
if ($publishedOnly) {
|
||||
$q->andWhere($q->expr()->eq('p.isPublished', 1));
|
||||
}
|
||||
|
||||
$q->orderBy('p.title');
|
||||
|
||||
if (!empty($limit)) {
|
||||
$q->setFirstResult($start)
|
||||
->setMaxResults($limit);
|
||||
}
|
||||
|
||||
return $q->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
protected function addCatchAllWhereClause($q, $filter): array
|
||||
{
|
||||
return $this->addStandardCatchAllWhereClause(
|
||||
$q,
|
||||
$filter,
|
||||
[
|
||||
'p.title',
|
||||
'p.alias',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function addSearchCommandWhereClause($q, $filter): array
|
||||
{
|
||||
[$expr, $parameters] = $this->addStandardSearchCommandWhereClause($q, $filter);
|
||||
if ($expr) {
|
||||
return [$expr, $parameters];
|
||||
}
|
||||
|
||||
$command = $filter->command;
|
||||
$unique = $this->generateRandomParameterName();
|
||||
$returnParameter = false; // returning a parameter that is not used will lead to a Doctrine error
|
||||
|
||||
switch ($command) {
|
||||
case $this->translator->trans('mautic.page.searchcommand.isexpired'):
|
||||
case $this->translator->trans('mautic.page.searchcommand.isexpired', [], null, 'en_US'):
|
||||
$expr = sprintf(
|
||||
"(p.isPublished = :%1\$s AND p.publishDown IS NOT NULL AND p.publishDown <> '' AND p.publishDown < CURRENT_TIMESTAMP())",
|
||||
$unique
|
||||
);
|
||||
$forceParameters = [$unique => true];
|
||||
break;
|
||||
case $this->translator->trans('mautic.page.searchcommand.ispending'):
|
||||
case $this->translator->trans('mautic.page.searchcommand.ispending', [], null, 'en_US'):
|
||||
$expr = sprintf(
|
||||
"(p.isPublished = :%1\$s AND p.publishUp IS NOT NULL AND p.publishUp <> '' AND p.publishUp > CURRENT_TIMESTAMP())",
|
||||
$unique
|
||||
);
|
||||
$forceParameters = [$unique => true];
|
||||
break;
|
||||
case $this->translator->trans('mautic.core.searchcommand.lang'):
|
||||
case $this->translator->trans('mautic.core.searchcommand.lang', [], null, 'en_US'):
|
||||
$langUnique = $this->generateRandomParameterName();
|
||||
$langValue = $filter->string.'_%';
|
||||
$forceParameters = [
|
||||
$langUnique => $langValue,
|
||||
$unique => $filter->string,
|
||||
];
|
||||
$expr = '('.$q->expr()->eq('p.language', ":$unique").' OR '.$q->expr()->like('p.language', ":$langUnique").')';
|
||||
$returnParameter = true;
|
||||
break;
|
||||
case $this->translator->trans('mautic.page.searchcommand.isprefcenter'):
|
||||
case $this->translator->trans('mautic.page.searchcommand.isprefcenter', [], null, 'en_US'):
|
||||
$expr = $q->expr()->eq('p.isPreferenceCenter', ":$unique");
|
||||
$forceParameters = [$unique => true];
|
||||
break;
|
||||
case $this->translator->trans('mautic.project.searchcommand.name'):
|
||||
case $this->translator->trans('mautic.project.searchcommand.name', [], null, 'en_US'):
|
||||
return $this->handleProjectFilter(
|
||||
$this->_em->getConnection()->createQueryBuilder(),
|
||||
'page_id',
|
||||
'page_projects_xref',
|
||||
$this->getTableAlias(),
|
||||
$filter->string,
|
||||
$filter->not
|
||||
);
|
||||
}
|
||||
|
||||
if ($expr && $filter->not) {
|
||||
$expr = $q->expr()->not($expr);
|
||||
}
|
||||
|
||||
if (!empty($forceParameters)) {
|
||||
$parameters = $forceParameters;
|
||||
} elseif ($returnParameter) {
|
||||
$string = ($filter->strict) ? $filter->string : "%{$filter->string}%";
|
||||
$parameters = ["$unique" => $string];
|
||||
}
|
||||
|
||||
return [$expr, $parameters];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSearchCommands(): array
|
||||
{
|
||||
$commands = [
|
||||
'mautic.core.searchcommand.ispublished',
|
||||
'mautic.core.searchcommand.isunpublished',
|
||||
'mautic.core.searchcommand.isuncategorized',
|
||||
'mautic.core.searchcommand.ismine',
|
||||
'mautic.page.searchcommand.isexpired',
|
||||
'mautic.page.searchcommand.ispending',
|
||||
'mautic.core.searchcommand.category',
|
||||
'mautic.core.searchcommand.lang',
|
||||
'mautic.page.searchcommand.isprefcenter',
|
||||
'mautic.project.searchcommand.name',
|
||||
];
|
||||
|
||||
return array_merge($commands, parent::getSearchCommands());
|
||||
}
|
||||
|
||||
protected function getDefaultOrder(): array
|
||||
{
|
||||
return [
|
||||
['p.title', 'ASC'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 'p';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets variant_start_date and variant_hits.
|
||||
*/
|
||||
public function resetVariants($relatedIds, $date): void
|
||||
{
|
||||
if (!is_array($relatedIds)) {
|
||||
$relatedIds = [(int) $relatedIds];
|
||||
}
|
||||
|
||||
$qb = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$qb->update(MAUTIC_TABLE_PREFIX.'pages')
|
||||
->set('variant_hits', 0)
|
||||
->set('variant_start_date', ':date')
|
||||
->setParameter('date', $date)
|
||||
->where(
|
||||
$qb->expr()->in('id', $relatedIds)
|
||||
)
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Up the hit count.
|
||||
*
|
||||
* @param int $increaseBy
|
||||
* @param bool|false $unique
|
||||
* @param bool|false $variant
|
||||
*/
|
||||
public function upHitCount($id, $increaseBy = 1, $unique = false, $variant = false): void
|
||||
{
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'pages')
|
||||
->set('hits', 'hits + '.(int) $increaseBy)
|
||||
->where('id = '.(int) $id);
|
||||
|
||||
if ($unique) {
|
||||
$q->set('unique_hits', 'unique_hits + '.(int) $increaseBy);
|
||||
}
|
||||
|
||||
if ($variant) {
|
||||
$q->set('variant_hits', 'variant_hits + '.(int) $increaseBy);
|
||||
}
|
||||
|
||||
$q->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\FormEntity;
|
||||
|
||||
class Redirect extends FormEntity
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $redirectId;
|
||||
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $hits = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $uniqueHits = 0;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection<int, Trackable>
|
||||
*/
|
||||
private $trackables;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->trackables = new ArrayCollection();
|
||||
}
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('page_redirects')
|
||||
->setCustomRepositoryClass(RedirectRepository::class);
|
||||
|
||||
$builder->addBigIntIdField();
|
||||
|
||||
$builder->createField('redirectId', 'string')
|
||||
->columnName('redirect_id')
|
||||
->length(25)
|
||||
->build();
|
||||
|
||||
$builder->addField('url', 'text');
|
||||
|
||||
$builder->addField('hits', 'integer');
|
||||
|
||||
$builder->createField('uniqueHits', 'integer')
|
||||
->columnName('unique_hits')
|
||||
->build();
|
||||
|
||||
$builder->createOneToMany('trackables', 'Trackable')
|
||||
->mappedBy('redirect')
|
||||
->fetchExtraLazy()
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('redirect')
|
||||
->addListProperties(
|
||||
[
|
||||
'id',
|
||||
'redirectId',
|
||||
'url',
|
||||
]
|
||||
)
|
||||
->addProperties(
|
||||
[
|
||||
'hits',
|
||||
'uniqueHits',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRedirectId()
|
||||
{
|
||||
return $this->redirectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $redirectId
|
||||
*/
|
||||
public function setRedirectId($redirectId = null): void
|
||||
{
|
||||
if (null === $redirectId) {
|
||||
$redirectId = substr(hash('sha1', uniqid(mt_rand())), 0, 25);
|
||||
}
|
||||
$this->redirectId = $redirectId;
|
||||
}
|
||||
|
||||
public function getUrl(): string
|
||||
{
|
||||
return trim($this->url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*/
|
||||
public function setUrl($url): void
|
||||
{
|
||||
$this->url = trim($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hits.
|
||||
*
|
||||
* @param int $hits
|
||||
*/
|
||||
public function setHits($hits): Redirect
|
||||
{
|
||||
$this->hits = $hits;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hits.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHits()
|
||||
{
|
||||
return $this->hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set uniqueHits.
|
||||
*
|
||||
* @param int $uniqueHits
|
||||
*/
|
||||
public function setUniqueHits($uniqueHits): Redirect
|
||||
{
|
||||
$this->uniqueHits = $uniqueHits;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uniqueHits.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUniqueHits()
|
||||
{
|
||||
return $this->uniqueHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection
|
||||
*/
|
||||
public function getTrackableList()
|
||||
{
|
||||
return $this->trackables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayCollection $trackables
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function setTrackables($trackables)
|
||||
{
|
||||
$this->trackables = $trackables;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Redirect>
|
||||
*/
|
||||
class RedirectRepository extends CommonRepository
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function findByUrls(array $urls)
|
||||
{
|
||||
$q = $this->createQueryBuilder('r');
|
||||
|
||||
$expr = $q->expr()->andX(
|
||||
$q->expr()->in('r.url', ':urls')
|
||||
);
|
||||
|
||||
$q->where($expr)
|
||||
->setParameter('urls', $urls);
|
||||
|
||||
return $q->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Up the hit count.
|
||||
*
|
||||
* @param int $increaseBy
|
||||
* @param bool|false $unique
|
||||
*/
|
||||
public function upHitCount($id, $increaseBy = 1, $unique = false): void
|
||||
{
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'page_redirects')
|
||||
->set('hits', 'hits + '.(int) $increaseBy)
|
||||
->where('id = '.(int) $id);
|
||||
|
||||
if ($unique) {
|
||||
$q->set('unique_hits', 'unique_hits + '.(int) $increaseBy);
|
||||
}
|
||||
|
||||
$q->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
* @param int|null $createdByUserId
|
||||
* @param int|null $companyId
|
||||
* @param int|null $campaignId
|
||||
* @param int|null $segmentId
|
||||
*/
|
||||
public function getMostHitEmailRedirects(
|
||||
$limit,
|
||||
\DateTime $dateFrom,
|
||||
\DateTime $dateTo,
|
||||
$createdByUserId = null,
|
||||
$companyId = null,
|
||||
$campaignId = null,
|
||||
$segmentId = null,
|
||||
): array {
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$q->addSelect('pr.url')
|
||||
->addSelect('count(ph.id) as hits')
|
||||
->addSelect('count(distinct ph.tracking_id) as unique_hits')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_hits', 'ph')
|
||||
->join('ph', MAUTIC_TABLE_PREFIX.'page_redirects', 'pr', 'pr.id = ph.redirect_id')
|
||||
->join('ph', MAUTIC_TABLE_PREFIX.'email_stats', 'es', 'ph.source = \'email\' and ph.source_id = es.email_id and ph.lead_id = es.lead_id')
|
||||
->join('es', MAUTIC_TABLE_PREFIX.'emails', 'e', 'es.email_id = e.id')
|
||||
->addSelect('e.id AS email_id')
|
||||
->addSelect('e.name AS email_name');
|
||||
|
||||
if (null !== $createdByUserId) {
|
||||
$q->andWhere('e.created_by = :userId')
|
||||
->setParameter('userId', $createdByUserId);
|
||||
}
|
||||
|
||||
$q->andWhere('ph.date_hit BETWEEN :dateFrom AND :dateTo')
|
||||
->setParameter('dateFrom', $dateFrom->format('Y-m-d H:i:s'))
|
||||
->setParameter('dateTo', $dateTo->format('Y-m-d H:i:s'));
|
||||
|
||||
$q->leftJoin('es', MAUTIC_TABLE_PREFIX.'campaign_events', 'ce', 'es.source = "campaign.event" and es.source_id = ce.id')
|
||||
->leftJoin('ce', MAUTIC_TABLE_PREFIX.'campaigns', 'campaign', 'ce.campaign_id = campaign.id')
|
||||
->addSelect('campaign.id AS campaign_id')
|
||||
->addSelect('campaign.name AS campaign_name');
|
||||
|
||||
if (null !== $campaignId) {
|
||||
$q->andWhere('ce.campaign_id = :campaignId')
|
||||
->setParameter('campaignId', $campaignId);
|
||||
}
|
||||
|
||||
if (!empty($companyId)) {
|
||||
$sb = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$sb->select('null')
|
||||
->from(MAUTIC_TABLE_PREFIX.'companies_leads', 'cl')
|
||||
->where(
|
||||
$sb->expr()->and(
|
||||
$sb->expr()->eq('cl.company_id', ':companyId'),
|
||||
$sb->expr()->eq('cl.lead_id', 'ph.lead_id')
|
||||
)
|
||||
);
|
||||
|
||||
$q->andWhere(
|
||||
sprintf('EXISTS (%s)', $sb->getSQL())
|
||||
)
|
||||
->setParameter('companyId', $companyId);
|
||||
}
|
||||
|
||||
if (null !== $segmentId) {
|
||||
$sb = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$sb->select('null')
|
||||
->from(MAUTIC_TABLE_PREFIX.'lead_lists_leads', 'lll')
|
||||
->where(
|
||||
$sb->expr()->and(
|
||||
$sb->expr()->eq('lll.leadlist_id', ':segmentId'),
|
||||
$sb->expr()->eq('lll.lead_id', 'ph.lead_id'),
|
||||
$sb->expr()->eq('lll.manually_removed', 0)
|
||||
)
|
||||
);
|
||||
|
||||
$q->andWhere(
|
||||
sprintf('EXISTS (%s)', $sb->getSQL())
|
||||
)
|
||||
->setParameter('segmentId', $segmentId);
|
||||
}
|
||||
|
||||
$q->groupBy('pr.id, pr.url, e.id, e.name, campaign.id, campaign.name');
|
||||
|
||||
$q->setMaxResults($limit);
|
||||
|
||||
$q->orderBy('hits', 'DESC');
|
||||
|
||||
return $q->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
|
||||
class Trackable
|
||||
{
|
||||
/**
|
||||
* @var Redirect
|
||||
*/
|
||||
private $redirect;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $channel;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $channelId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $hits = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $uniqueHits = 0;
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('channel_url_trackables')
|
||||
->setCustomRepositoryClass(TrackableRepository::class)
|
||||
->addIndex(['channel', 'channel_id'], 'channel_url_trackable_search');
|
||||
|
||||
$builder->createManyToOne('redirect', Redirect::class)
|
||||
->addJoinColumn('redirect_id', 'id', true, false, 'CASCADE')
|
||||
->cascadePersist()
|
||||
->inversedBy('trackables')
|
||||
->isPrimaryKey()
|
||||
->build();
|
||||
|
||||
$builder->createField('channelId', 'integer')
|
||||
->columnName('channel_id')
|
||||
->makePrimaryKey()
|
||||
->build();
|
||||
|
||||
$builder->addField('channel', 'string');
|
||||
|
||||
$builder->addField('hits', 'integer');
|
||||
|
||||
$builder->addNamedField('uniqueHits', 'integer', 'unique_hits');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('trackable')
|
||||
->addListProperties(
|
||||
[
|
||||
'redirect',
|
||||
'channelId',
|
||||
'channel',
|
||||
'hits',
|
||||
'uniqueHits',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getRedirect()
|
||||
{
|
||||
return $this->redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Trackable
|
||||
*/
|
||||
public function setRedirect(Redirect $redirect)
|
||||
{
|
||||
$this->redirect = $redirect;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannel()
|
||||
{
|
||||
return $this->channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $channel
|
||||
*
|
||||
* @return Trackable
|
||||
*/
|
||||
public function setChannel($channel)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getChannelId()
|
||||
{
|
||||
return $this->channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $channelId
|
||||
*
|
||||
* @return Trackable
|
||||
*/
|
||||
public function setChannelId($channelId)
|
||||
{
|
||||
$this->channelId = $channelId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getHits()
|
||||
{
|
||||
return $this->hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $hits
|
||||
*
|
||||
* @return Trackable
|
||||
*/
|
||||
public function setHits($hits)
|
||||
{
|
||||
$this->hits = $hits;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUniqueHits()
|
||||
{
|
||||
return $this->uniqueHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uniqueHits
|
||||
*
|
||||
* @return Trackable
|
||||
*/
|
||||
public function setUniqueHits($uniqueHits)
|
||||
{
|
||||
$this->uniqueHits = $uniqueHits;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Trackable>
|
||||
*/
|
||||
class TrackableRepository extends CommonRepository
|
||||
{
|
||||
/**
|
||||
* Find redirects that are trackable.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function findByChannel($channel, $channelId): array
|
||||
{
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$tableAlias = $this->getTableAlias();
|
||||
|
||||
return $q->select('r.redirect_id, r.url, r.id, '.$tableAlias.'.hits, '.$tableAlias.'.unique_hits')
|
||||
->from(MAUTIC_TABLE_PREFIX.'page_redirects', 'r')
|
||||
->innerJoin('r', MAUTIC_TABLE_PREFIX.'channel_url_trackables', $tableAlias,
|
||||
$q->expr()->and(
|
||||
$q->expr()->eq('r.id', 't.redirect_id'),
|
||||
$q->expr()->eq('t.channel', ':channel'),
|
||||
$q->expr()->eq('t.channel_id', (int) $channelId)
|
||||
)
|
||||
)
|
||||
->setParameter('channel', $channel)
|
||||
->orderBy('r.url')
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Trackable by Redirect URL.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findByUrl($url, $channel, $channelId)
|
||||
{
|
||||
$alias = $this->getTableAlias();
|
||||
$q = $this->createQueryBuilder($alias)
|
||||
->innerJoin("$alias.redirect", 'r');
|
||||
|
||||
$q->where(
|
||||
$q->expr()->andX(
|
||||
$q->expr()->eq("$alias.channel", ':channel'),
|
||||
$q->expr()->eq("$alias.channelId", (int) $channelId),
|
||||
$q->expr()->eq('r.url', ':url')
|
||||
)
|
||||
)
|
||||
->setParameter('url', $url)
|
||||
->setParameter('channel', $channel);
|
||||
|
||||
$result = $q->getQuery()->getResult();
|
||||
|
||||
return ($result) ? $result[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of Trackable entities by Redirect URLs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findByUrls(array $urls, $channel, $channelId)
|
||||
{
|
||||
$alias = $this->getTableAlias();
|
||||
$q = $this->createQueryBuilder($alias)
|
||||
->innerJoin("$alias.redirect", 'r');
|
||||
|
||||
$q->where(
|
||||
$q->expr()->andX(
|
||||
$q->expr()->eq("$alias.channel", ':channel'),
|
||||
$q->expr()->eq("$alias.channelId", (int) $channelId),
|
||||
$q->expr()->in('r.url', ':urls')
|
||||
)
|
||||
)
|
||||
->setParameter('urls', $urls)
|
||||
->setParameter('channel', $channel);
|
||||
|
||||
return $q->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Up the hit count.
|
||||
*
|
||||
* @param int $increaseBy
|
||||
* @param bool $unique
|
||||
*/
|
||||
public function upHitCount($redirectId, $channel, $channelId, $increaseBy = 1, $unique = false): void
|
||||
{
|
||||
$q = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'channel_url_trackables')
|
||||
->set('hits', 'hits + '.(int) $increaseBy)
|
||||
->where(
|
||||
$q->expr()->and(
|
||||
$q->expr()->eq('redirect_id', (int) $redirectId),
|
||||
$q->expr()->eq('channel', ':channel'),
|
||||
$q->expr()->eq('channel_id', (int) $channelId)
|
||||
)
|
||||
)
|
||||
->setParameter('channel', $channel);
|
||||
|
||||
if ($unique) {
|
||||
$q->set('unique_hits', 'unique_hits + '.(int) $increaseBy);
|
||||
}
|
||||
|
||||
$q->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hit count.
|
||||
*
|
||||
* @param bool $combined
|
||||
* @param string $countColumn
|
||||
*
|
||||
* @return array|int
|
||||
*/
|
||||
public function getCount($channel, $channelIds, $listId, ?ChartQuery $chartQuery = null, $combined = false, $countColumn = 'ph.id')
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder()
|
||||
->select('count('.$countColumn.') as click_count')
|
||||
->from(MAUTIC_TABLE_PREFIX.'channel_url_trackables', 'cut')
|
||||
->innerJoin('cut', MAUTIC_TABLE_PREFIX.'page_hits', 'ph', 'ph.redirect_id = cut.redirect_id AND ph.source = cut.channel AND ph.source_id = cut.channel_id');
|
||||
|
||||
$q->where(
|
||||
'cut.channel = :channel'
|
||||
)->setParameter('channel', $channel);
|
||||
|
||||
if ($channelIds) {
|
||||
if (!is_array($channelIds)) {
|
||||
$channelIds = [(int) $channelIds];
|
||||
}
|
||||
$q->andWhere(
|
||||
$q->expr()->in('cut.channel_id', $channelIds)
|
||||
);
|
||||
}
|
||||
|
||||
if ($listId) {
|
||||
if (!$combined) {
|
||||
$q->innerJoin('ph', MAUTIC_TABLE_PREFIX.'lead_lists_leads', 'cs', 'cs.lead_id = ph.lead_id');
|
||||
|
||||
if (true === $listId) {
|
||||
$q->addSelect('cs.leadlist_id')
|
||||
->groupBy('cs.leadlist_id');
|
||||
} elseif (is_array($listId)) {
|
||||
$q->andWhere(
|
||||
$q->expr()->in('cs.leadlist_id', array_map('intval', $listId))
|
||||
);
|
||||
|
||||
$q->addSelect('cs.leadlist_id')
|
||||
->groupBy('cs.leadlist_id');
|
||||
} else {
|
||||
$q->andWhere('cs.leadlist_id = :list_id')
|
||||
->setParameter('list_id', $listId);
|
||||
}
|
||||
} else {
|
||||
$subQ = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$subQ->select('distinct(list.lead_id)')
|
||||
->from(MAUTIC_TABLE_PREFIX.'lead_lists_leads', 'list')
|
||||
->andWhere(
|
||||
$q->expr()->in('list.leadlist_id', array_map('intval', $listId))
|
||||
);
|
||||
|
||||
$q->innerJoin('ph', sprintf('(%s)', $subQ->getSQL()), 'cs', 'cs.lead_id = ph.lead_id');
|
||||
}
|
||||
}
|
||||
|
||||
if ($chartQuery) {
|
||||
$chartQuery->applyDateFilters($q, 'date_hit', 'ph');
|
||||
}
|
||||
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
if ((true === $listId || is_array($listId)) && !$combined) {
|
||||
// Return array of results
|
||||
$byList = [];
|
||||
foreach ($results as $result) {
|
||||
$byList[$result['leadlist_id']] = $result['click_count'];
|
||||
}
|
||||
|
||||
return $byList;
|
||||
}
|
||||
|
||||
return (isset($results[0])) ? $results[0]['click_count'] : 0;
|
||||
}
|
||||
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 't';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,776 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\IpAddress;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
|
||||
class VideoHit
|
||||
{
|
||||
public const TABLE_NAME = 'video_hits';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $guid;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateHit;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateLeft;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $timeWatched;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $duration;
|
||||
|
||||
/**
|
||||
* @var Redirect
|
||||
*/
|
||||
private $redirect;
|
||||
|
||||
/**
|
||||
* @var Lead|null
|
||||
*/
|
||||
private $lead;
|
||||
|
||||
/**
|
||||
* @var IpAddress|null
|
||||
*/
|
||||
private $ipAddress;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $region;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $city;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $isp;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $organization;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $code;
|
||||
|
||||
private $referer;
|
||||
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $userAgent;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $remoteHost;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $pageLanguage;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private $browserLanguages = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $channel;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $channelId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $query = [];
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable(self::TABLE_NAME)
|
||||
->setCustomRepositoryClass(VideoHitRepository::class)
|
||||
->addIndex(['date_hit'], 'video_date_hit')
|
||||
->addIndex(['channel', 'channel_id'], 'video_channel_search')
|
||||
->addIndex(['guid', 'lead_id'], 'video_guid_lead_search');
|
||||
|
||||
$builder->addId();
|
||||
|
||||
$builder->createField('dateHit', 'datetime')
|
||||
->columnName('date_hit')
|
||||
->build();
|
||||
|
||||
$builder->createField('dateLeft', 'datetime')
|
||||
->columnName('date_left')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addLead(true, 'SET NULL');
|
||||
|
||||
$builder->addIpAddress(true);
|
||||
|
||||
$builder->createField('country', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('region', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('city', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('isp', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('organization', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addField('code', 'integer');
|
||||
|
||||
$builder->createField('referer', 'text')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('url', 'text')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('userAgent', 'text')
|
||||
->columnName('user_agent')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('remoteHost', 'string')
|
||||
->columnName('remote_host')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('guid', 'string')
|
||||
->columnName('guid')
|
||||
->build();
|
||||
|
||||
$builder->createField('pageLanguage', 'string')
|
||||
->columnName('page_language')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('browserLanguages', 'array')
|
||||
->columnName('browser_languages')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('channel', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('channelId', 'integer')
|
||||
->columnName('channel_id')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('timeWatched', 'integer')
|
||||
->columnName('time_watched')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('duration', 'integer')
|
||||
->columnName('duration')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('query', 'array');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('hit')
|
||||
->addProperties(
|
||||
[
|
||||
'dateHit',
|
||||
'dateLeft',
|
||||
'lead',
|
||||
'ipAddress',
|
||||
'country',
|
||||
'region',
|
||||
'city',
|
||||
'isp',
|
||||
'code',
|
||||
'referer',
|
||||
'url',
|
||||
'urlTitle',
|
||||
'userAgent',
|
||||
'remoteHost',
|
||||
'pageLanguage',
|
||||
'browserLanguages',
|
||||
'source',
|
||||
'sourceId',
|
||||
'query',
|
||||
'timeWatched',
|
||||
'guid',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dateHit.
|
||||
*
|
||||
* @param \DateTime $dateHit
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setDateHit($dateHit)
|
||||
{
|
||||
$this->dateHit = $dateHit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dateHit.
|
||||
*
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateHit()
|
||||
{
|
||||
return $this->dateHit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateLeft()
|
||||
{
|
||||
return $this->dateLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dateLeft
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setDateLeft($dateLeft)
|
||||
{
|
||||
$this->dateLeft = $dateLeft;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set country.
|
||||
*
|
||||
* @param string $country
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setCountry($country)
|
||||
{
|
||||
$this->country = $country;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get country.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCountry()
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set region.
|
||||
*
|
||||
* @param string $region
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setRegion($region)
|
||||
{
|
||||
$this->region = $region;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get region.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRegion()
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set city.
|
||||
*
|
||||
* @param string $city
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setCity($city)
|
||||
{
|
||||
$this->city = $city;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get city.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCity()
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set isp.
|
||||
*
|
||||
* @param string $isp
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setIsp($isp)
|
||||
{
|
||||
$this->isp = $isp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get isp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIsp()
|
||||
{
|
||||
return $this->isp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set organization.
|
||||
*
|
||||
* @param string $organization
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setOrganization($organization)
|
||||
{
|
||||
$this->organization = $organization;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get organization.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrganization()
|
||||
{
|
||||
return $this->organization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set code.
|
||||
*
|
||||
* @param int $code
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set referer.
|
||||
*
|
||||
* @param string $referer
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setReferer($referer)
|
||||
{
|
||||
$this->referer = $referer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get referer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReferer()
|
||||
{
|
||||
return $this->referer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set url.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set userAgent.
|
||||
*
|
||||
* @param string $userAgent
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setUserAgent($userAgent)
|
||||
{
|
||||
$this->userAgent = $userAgent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get userAgent.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAgent()
|
||||
{
|
||||
return $this->userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set remoteHost.
|
||||
*
|
||||
* @param string $remoteHost
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setRemoteHost($remoteHost)
|
||||
{
|
||||
$this->remoteHost = $remoteHost;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remoteHost.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteHost()
|
||||
{
|
||||
return $this->remoteHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setIpAddress(IpAddress $ipAddress)
|
||||
{
|
||||
$this->ipAddress = $ipAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IpAddress
|
||||
*/
|
||||
public function getIpAddress()
|
||||
{
|
||||
return $this->ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pageLanguage.
|
||||
*
|
||||
* @param string $pageLanguage
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setPageLanguage($pageLanguage)
|
||||
{
|
||||
$this->pageLanguage = $pageLanguage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pageLanguage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPageLanguage()
|
||||
{
|
||||
return $this->pageLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set browserLanguages.
|
||||
*
|
||||
* @param array<string> $browserLanguages
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setBrowserLanguages($browserLanguages)
|
||||
{
|
||||
$this->browserLanguages = $browserLanguages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get browserLanguages.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getBrowserLanguages()
|
||||
{
|
||||
return $this->browserLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Lead
|
||||
*/
|
||||
public function getLead()
|
||||
{
|
||||
return $this->lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setLead(Lead $lead)
|
||||
{
|
||||
$this->lead = $lead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannel()
|
||||
{
|
||||
return $this->channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $channel
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setChannel($channel)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getChannelId()
|
||||
{
|
||||
return $this->channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $channelId
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setChannelId($channelId)
|
||||
{
|
||||
$this->channelId = (int) $channelId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getRedirect()
|
||||
{
|
||||
return $this->redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setRedirect(Redirect $redirect)
|
||||
{
|
||||
$this->redirect = $redirect;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setQuery($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeWatched()
|
||||
{
|
||||
return $this->timeWatched;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setTimeWatched($timeWatched)
|
||||
{
|
||||
$this->timeWatched = $timeWatched;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getGuid()
|
||||
{
|
||||
return $this->guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $guid
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setGuid($guid)
|
||||
{
|
||||
$this->guid = $guid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDuration()
|
||||
{
|
||||
return $this->duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $duration
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function setDuration($duration)
|
||||
{
|
||||
$this->duration = $duration;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\PageBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Entity\TimelineTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<VideoHit>
|
||||
*/
|
||||
class VideoHitRepository extends CommonRepository
|
||||
{
|
||||
use TimelineTrait;
|
||||
|
||||
/**
|
||||
* Get video hit info for lead timeline.
|
||||
*
|
||||
* @param int|null $leadId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTimelineStats($leadId = null, array $options = [])
|
||||
{
|
||||
$query = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$query->select('h.id, h.url, h.date_hit, h.time_watched, h.duration, h.referer, h.user_agent')
|
||||
->from(MAUTIC_TABLE_PREFIX.'video_hits', 'h');
|
||||
|
||||
if ($leadId) {
|
||||
$query->where($query->expr()->eq('h.lead_id', (int) $leadId));
|
||||
}
|
||||
|
||||
if (isset($options['search']) && $options['search']) {
|
||||
$query->andWhere(
|
||||
$query->expr()->like('h.url', ':search')
|
||||
)->setParameter('search', '%'.$options['search'].'%');
|
||||
}
|
||||
|
||||
return $this->getTimelineResults($query, $options, 'h.url', 'h.date_hit', [], ['date_hit'], null, 'h.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $guid
|
||||
*
|
||||
* @return VideoHit
|
||||
*/
|
||||
public function getHitForLeadByGuid(Lead $lead, $guid)
|
||||
{
|
||||
$result = $this->findOneBy(['guid' => $guid, 'lead' => $lead]);
|
||||
|
||||
return $result ?: new VideoHit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lead's page hits.
|
||||
*
|
||||
* @param int $leadId
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getLeadHits($leadId, array $options = [])
|
||||
{
|
||||
$query = $this->createQueryBuilder('h');
|
||||
$query->select('h.userAgent, h.dateHit, h.dateLeft, h.referer, h.channel, h.channelId, h.url, h.duration, h.query, h.timeWatched')
|
||||
->where('h.lead = :leadId')
|
||||
->setParameter('leadId', (int) $leadId);
|
||||
|
||||
if (isset($options['url']) && $options['url']) {
|
||||
$query->andWhere($query->expr()->eq('h.url', $query->expr()->literal($options['url'])));
|
||||
}
|
||||
|
||||
return $query->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count stats from hit times.
|
||||
*
|
||||
* @param array $times
|
||||
*/
|
||||
public function countStats($times): array
|
||||
{
|
||||
return [
|
||||
'sum' => array_sum($times),
|
||||
'min' => count($times) ? min($times) : 0,
|
||||
'max' => count($times) ? max($times) : 0,
|
||||
'average' => count($times) ? round(array_sum($times) / count($times)) : 0,
|
||||
'count' => count($times),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of referers ordered by it's count.
|
||||
*
|
||||
* @param \Doctrine\DBAL\Query\QueryBuilder $query
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getReferers($query, $limit = 10, $offset = 0): array
|
||||
{
|
||||
$query->select('h.referer, count(h.referer) as sessions')
|
||||
->groupBy('h.referer')
|
||||
->orderBy('sessions', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $query->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates lead ID (e.g. after a lead merge).
|
||||
*/
|
||||
public function updateLead($fromLeadId, $toLeadId): void
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'video_hits')
|
||||
->set('lead_id', (int) $toLeadId)
|
||||
->where('lead_id = '.(int) $fromLeadId)
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user