344 lines
10 KiB
PHP
Executable File
344 lines
10 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\DynamicContentBundle\Model;
|
|
|
|
use Doctrine\DBAL\Query\QueryBuilder;
|
|
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
|
|
use Mautic\CoreBundle\Helper\Chart\LineChart;
|
|
use Mautic\CoreBundle\Model\AjaxLookupModelInterface;
|
|
use Mautic\CoreBundle\Model\FormModel;
|
|
use Mautic\CoreBundle\Model\GlobalSearchInterface;
|
|
use Mautic\CoreBundle\Model\TranslationModelTrait;
|
|
use Mautic\CoreBundle\Model\VariantModelTrait;
|
|
use Mautic\DynamicContentBundle\DynamicContentEvents;
|
|
use Mautic\DynamicContentBundle\Entity\DynamicContent;
|
|
use Mautic\DynamicContentBundle\Entity\DynamicContentRepository;
|
|
use Mautic\DynamicContentBundle\Entity\Stat;
|
|
use Mautic\DynamicContentBundle\Event\DynamicContentEvent;
|
|
use Mautic\DynamicContentBundle\Form\Type\DynamicContentType;
|
|
use Mautic\LeadBundle\Entity\Lead;
|
|
use Symfony\Component\Form\FormFactoryInterface;
|
|
use Symfony\Component\Form\FormInterface;
|
|
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
|
use Symfony\Contracts\EventDispatcher\Event;
|
|
|
|
/**
|
|
* @extends FormModel<DynamicContent>
|
|
*
|
|
* @implements AjaxLookupModelInterface<DynamicContent>
|
|
*/
|
|
class DynamicContentModel extends FormModel implements AjaxLookupModelInterface, GlobalSearchInterface
|
|
{
|
|
use VariantModelTrait;
|
|
use TranslationModelTrait;
|
|
|
|
/**
|
|
* Retrieve the permissions base.
|
|
*/
|
|
public function getPermissionBase(): string
|
|
{
|
|
return 'dynamiccontent:dynamiccontents';
|
|
}
|
|
|
|
/**
|
|
* @return DynamicContentRepository
|
|
*/
|
|
public function getRepository()
|
|
{
|
|
/** @var DynamicContentRepository $repo */
|
|
$repo = $this->em->getRepository(DynamicContent::class);
|
|
|
|
$repo->setTranslator($this->translator);
|
|
$repo->setCurrentUser($this->userHelper->getUser());
|
|
|
|
return $repo;
|
|
}
|
|
|
|
/**
|
|
* @return \Mautic\DynamicContentBundle\Entity\StatRepository
|
|
*/
|
|
public function getStatRepository()
|
|
{
|
|
return $this->em->getRepository(Stat::class);
|
|
}
|
|
|
|
/**
|
|
* @param object $entity
|
|
* @param bool $unlock
|
|
*/
|
|
public function saveEntity($entity, $unlock = true): void
|
|
{
|
|
parent::saveEntity($entity, $unlock);
|
|
|
|
$this->postTranslationEntitySave($entity);
|
|
}
|
|
|
|
public function getEntity($id = null): ?DynamicContent
|
|
{
|
|
if (null === $id) {
|
|
return new DynamicContent();
|
|
}
|
|
|
|
return parent::getEntity($id);
|
|
}
|
|
|
|
public function checkEntityBySlotName(string $slotName, ?string $type = null, string $typeCondition = '=',
|
|
?int $skipId = null): bool
|
|
{
|
|
$qb = $this->em->getConnection()->createQueryBuilder();
|
|
|
|
$qb->select('1')
|
|
->from(MAUTIC_TABLE_PREFIX.'dynamic_content')
|
|
->where($qb->expr()->eq('slot_name', ':slot_name'))
|
|
->setParameter('slot_name', $slotName)
|
|
->setMaxResults(1);
|
|
|
|
if (!empty($type)) {
|
|
if (!in_array($typeCondition, ['=', '<>', '!='], true)) {
|
|
throw new \InvalidArgumentException("Invalid operator '$typeCondition'");
|
|
}
|
|
|
|
$qb->andWhere("type {$typeCondition} :type");
|
|
$qb->setParameter('type', $type);
|
|
}
|
|
|
|
if (null !== $skipId) {
|
|
$qb->andWhere('id != :id');
|
|
$qb->setParameter('id', $skipId);
|
|
}
|
|
|
|
return (bool) $qb->executeQuery()->fetchOne();
|
|
}
|
|
|
|
/**
|
|
* @param string|null $action
|
|
* @param array $options
|
|
*
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
public function createForm($entity, FormFactoryInterface $formFactory, $action = null, $options = []): FormInterface
|
|
{
|
|
if (!$entity instanceof DynamicContent) {
|
|
throw new \InvalidArgumentException('Entity must be of class DynamicContent');
|
|
}
|
|
|
|
if (!empty($action)) {
|
|
$options['action'] = $action;
|
|
}
|
|
|
|
return $formFactory->create(DynamicContentType::class, $entity, $options);
|
|
}
|
|
|
|
public function setSlotContentForLead(DynamicContent $dwc, Lead $lead, $slot): void
|
|
{
|
|
$qb = $this->em->getConnection()->createQueryBuilder();
|
|
|
|
$qb->insert(MAUTIC_TABLE_PREFIX.'dynamic_content_lead_data')
|
|
->values([
|
|
'lead_id' => $lead->getId(),
|
|
'dynamic_content_id' => $dwc->getId(),
|
|
'slot' => ':slot',
|
|
'date_added' => $qb->expr()->literal((new \DateTime())->format('Y-m-d H:i:s')),
|
|
])->setParameter('slot', $slot);
|
|
|
|
$qb->executeStatement();
|
|
}
|
|
|
|
/**
|
|
* @param string $slot
|
|
* @param Lead|array $lead
|
|
*
|
|
* @return array<string, mixed>|false
|
|
*/
|
|
public function getSlotContentForLead($slot, $lead)
|
|
{
|
|
if (!$lead) {
|
|
return [];
|
|
}
|
|
|
|
$qb = $this->em->getConnection()->createQueryBuilder();
|
|
|
|
$id = $lead instanceof Lead ? $lead->getId() : $lead['id'];
|
|
|
|
$qb->select('dc.id, dc.content')
|
|
->from(MAUTIC_TABLE_PREFIX.'dynamic_content', 'dc')
|
|
->leftJoin('dc', MAUTIC_TABLE_PREFIX.'dynamic_content_lead_data', 'dcld', 'dcld.dynamic_content_id = dc.id')
|
|
->andWhere($qb->expr()->eq('dcld.slot', ':slot'))
|
|
->andWhere($qb->expr()->eq('dcld.lead_id', ':lead_id'))
|
|
->andWhere($qb->expr()->eq('dc.is_published', 1))
|
|
->setParameter('slot', $slot)
|
|
->setParameter('lead_id', $id)
|
|
->orderBy('dcld.date_added', 'DESC')
|
|
->addOrderBy('dcld.id', 'DESC');
|
|
|
|
return $qb->executeQuery()->fetchAssociative();
|
|
}
|
|
|
|
/**
|
|
* @param Lead|array $lead
|
|
* @param string $source
|
|
*
|
|
* @return Stat|null
|
|
*/
|
|
public function createStatEntry(DynamicContent $dynamicContent, $lead, $source = null)
|
|
{
|
|
if (empty($lead)) {
|
|
return null;
|
|
}
|
|
|
|
if ($lead instanceof Lead && !$lead->getId()) {
|
|
return null;
|
|
}
|
|
|
|
if (is_array($lead)) {
|
|
if (empty($lead['id'])) {
|
|
return null;
|
|
}
|
|
|
|
$lead = $this->em->getReference(Lead::class, $lead['id']);
|
|
}
|
|
|
|
$stat = new Stat();
|
|
$stat->setDateSent(new \DateTime());
|
|
$stat->setLead($lead);
|
|
$stat->setDynamicContent($dynamicContent);
|
|
$stat->setSource($source);
|
|
|
|
$this->getStatRepository()->saveEntity($stat);
|
|
|
|
return $stat;
|
|
}
|
|
|
|
/**
|
|
* @throws MethodNotAllowedHttpException
|
|
*/
|
|
protected function dispatchEvent($action, &$entity, $isNew = false, ?Event $event = null): ?Event
|
|
{
|
|
if (!$entity instanceof DynamicContent) {
|
|
throw new MethodNotAllowedHttpException(['Dynamic Content']);
|
|
}
|
|
|
|
switch ($action) {
|
|
case 'pre_save':
|
|
$name = DynamicContentEvents::PRE_SAVE;
|
|
break;
|
|
case 'post_save':
|
|
$name = DynamicContentEvents::POST_SAVE;
|
|
break;
|
|
case 'pre_delete':
|
|
$name = DynamicContentEvents::PRE_DELETE;
|
|
break;
|
|
case 'post_delete':
|
|
$name = DynamicContentEvents::POST_DELETE;
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
if ($this->dispatcher->hasListeners($name)) {
|
|
if (empty($event)) {
|
|
$event = new DynamicContentEvent($entity, $isNew);
|
|
$event->setEntityManager($this->em);
|
|
}
|
|
|
|
$this->dispatcher->dispatch($event, $name);
|
|
|
|
return $event;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Joins the page table and limits created_by to currently logged in user.
|
|
*/
|
|
public function limitQueryToCreator(QueryBuilder &$q): void
|
|
{
|
|
$q->join('t', MAUTIC_TABLE_PREFIX.'dynamic_content', 'd', 'd.id = t.dynamic_content_id')
|
|
->andWhere('d.created_by = :userId')
|
|
->setParameter('userId', $this->userHelper->getUser()->getId());
|
|
}
|
|
|
|
/**
|
|
* Get line chart data of hits.
|
|
*
|
|
* @param char $unit {@link php.net/manual/en/function.date.php#refsect1-function.date-parameters}
|
|
* @param string $dateFormat
|
|
* @param array $filter
|
|
* @param bool $canViewOthers
|
|
*/
|
|
public function getHitsLineChartData($unit, \DateTime $dateFrom, \DateTime $dateTo, $dateFormat = null, $filter = [], $canViewOthers = true): array
|
|
{
|
|
$flag = null;
|
|
|
|
if (isset($filter['flag'])) {
|
|
$flag = $filter['flag'];
|
|
unset($filter['flag']);
|
|
}
|
|
|
|
$chart = new LineChart($unit, $dateFrom, $dateTo, $dateFormat);
|
|
$query = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo);
|
|
|
|
if (!$flag || 'total_and_unique' === $flag) {
|
|
$q = $query->prepareTimeDataQuery('dynamic_content_stats', 'date_sent', $filter);
|
|
|
|
if (!$canViewOthers) {
|
|
$this->limitQueryToCreator($q);
|
|
}
|
|
|
|
$data = $query->loadAndBuildTimeData($q);
|
|
$chart->setDataset($this->translator->trans('mautic.dynamicContent.show.total.views'), $data);
|
|
}
|
|
|
|
if ('unique' === $flag || 'total_and_unique' === $flag) {
|
|
$q = $query->prepareTimeDataQuery('dynamic_content_stats', 'date_sent', $filter);
|
|
$q->groupBy('t.lead_id, t.date_sent');
|
|
|
|
if (!$canViewOthers) {
|
|
$this->limitQueryToCreator($q);
|
|
}
|
|
|
|
$data = $query->loadAndBuildTimeData($q);
|
|
$chart->setDataset($this->translator->trans('mautic.dynamicContent.show.unique.views'), $data);
|
|
}
|
|
|
|
return $chart->render();
|
|
}
|
|
|
|
/**
|
|
* @param string $filter
|
|
* @param int $limit
|
|
* @param int $start
|
|
* @param array $options
|
|
*
|
|
* @return mixed[]
|
|
*/
|
|
public function getLookupResults($type, $filter = '', $limit = 10, $start = 0, $options = []): array
|
|
{
|
|
$results = [];
|
|
switch ($type) {
|
|
case 'dynamicContent':
|
|
$entities = $this->getRepository()->getDynamicContentList(
|
|
$filter,
|
|
$limit,
|
|
$start,
|
|
$this->security->isGranted($this->getPermissionBase().':viewother'),
|
|
$options['top_level'] ?? false,
|
|
$options['ignore_ids'] ?? [],
|
|
$options['where'] ?? ''
|
|
);
|
|
|
|
foreach ($entities as $entity) {
|
|
$results[$entity['language']][$entity['id']] = $entity['name'];
|
|
}
|
|
|
|
// sort by language
|
|
ksort($results);
|
|
|
|
break;
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
}
|