Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,569 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\DynamicContentBundle\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\Common\Collections\ArrayCollection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Events;
|
||||
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\FiltersEntityTrait;
|
||||
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\DynamicContentBundle\DynamicContent\TypeList;
|
||||
use Mautic\DynamicContentBundle\Validator\Constraints\NoNesting;
|
||||
use Mautic\DynamicContentBundle\Validator\Constraints\SlotNameType;
|
||||
use Mautic\ProjectBundle\Entity\ProjectTrait;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
use Symfony\Component\Validator\Constraints\Count;
|
||||
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('dynamiccontent:dynamiccontents:viewown')"),
|
||||
new Post(security: "is_granted('dynamiccontent:dynamiccontents:create')"),
|
||||
new Get(security: "is_granted('dynamiccontent:dynamiccontents:viewown')"),
|
||||
new Put(security: "is_granted('dynamiccontent:dynamiccontents:editown')"),
|
||||
new Patch(security: "is_granted('dynamiccontent:dynamiccontents:editother')"),
|
||||
new Delete(security: "is_granted('dynamiccontent:dynamiccontents:deleteown')"),
|
||||
],
|
||||
normalizationContext: [
|
||||
'groups' => ['dynamicContent:read'],
|
||||
'swagger_definition_name' => 'Read',
|
||||
'api_included' => ['category', 'translationChildren'],
|
||||
],
|
||||
denormalizationContext: [
|
||||
'groups' => ['dynamicContent:write'],
|
||||
'swagger_definition_name' => 'Write',
|
||||
]
|
||||
)]
|
||||
/**
|
||||
* @use TranslationEntityTrait<DynamicContent>
|
||||
* @use VariantEntityTrait<DynamicContent>
|
||||
*/
|
||||
class DynamicContent extends FormEntity implements VariantEntityInterface, TranslationEntityInterface, UuidInterface
|
||||
{
|
||||
use TranslationEntityTrait;
|
||||
use VariantEntityTrait;
|
||||
use FiltersEntityTrait;
|
||||
use UuidTrait;
|
||||
use ProjectTrait;
|
||||
|
||||
public const ENTITY_NAME = 'dynamic_content';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['dynamicContent:read'])]
|
||||
private $id;
|
||||
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private ?string $name = null;
|
||||
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private string $type = TypeList::HTML;
|
||||
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private ?string $description = null;
|
||||
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private ?Category $category = null;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private $publishUp;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private $publishDown;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private $utmTags = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['dynamicContent:read'])]
|
||||
private $sentCount = 0;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection<Stat>
|
||||
*/
|
||||
#[Groups(['dynamicContent:read'])]
|
||||
private $stats;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private $isCampaignBased = true;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['dynamicContent:read', 'dynamicContent:write'])]
|
||||
private $slotName;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->stats = new ArrayCollection();
|
||||
$this->translationChildren = new ArrayCollection();
|
||||
$this->variantChildren = new ArrayCollection();
|
||||
$this->initializeProjects();
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->id = null;
|
||||
$this->sentCount = 0;
|
||||
$this->stats = new ArrayCollection();
|
||||
$this->translationChildren = new ArrayCollection();
|
||||
$this->variantChildren = new ArrayCollection();
|
||||
|
||||
parent::__clone();
|
||||
}
|
||||
|
||||
public function clearStats(): void
|
||||
{
|
||||
$this->stats = new ArrayCollection();
|
||||
}
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('dynamic_content')
|
||||
->addIndex(['is_campaign_based'], 'is_campaign_based_index')
|
||||
->addIndex(['slot_name'], 'slot_name_index')
|
||||
->setCustomRepositoryClass(DynamicContentRepository::class)
|
||||
->addLifecycleEvent('cleanSlotName', Events::prePersist)
|
||||
->addLifecycleEvent('cleanSlotName', Events::preUpdate);
|
||||
|
||||
$builder->addIdColumns();
|
||||
|
||||
$builder->addCategory();
|
||||
|
||||
$builder->addField(
|
||||
'type',
|
||||
Types::STRING,
|
||||
[
|
||||
'length' => 10,
|
||||
'default' => TypeList::HTML,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->addPublishDates();
|
||||
|
||||
$builder->createField('sentCount', 'integer')
|
||||
->columnName('sent_count')
|
||||
->build();
|
||||
|
||||
$builder->createField('content', 'text')
|
||||
->columnName('content')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('utmTags', Types::JSON)
|
||||
->columnName('utm_tags')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createOneToMany('stats', 'Stat')
|
||||
->setIndexBy('id')
|
||||
->mappedBy('dynamicContent')
|
||||
->cascadePersist()
|
||||
->fetchExtraLazy()
|
||||
->build();
|
||||
|
||||
self::addTranslationMetadata($builder, self::class);
|
||||
self::addVariantMetadata($builder, self::class);
|
||||
self::addFiltersMetadata($builder);
|
||||
|
||||
$builder->createField('isCampaignBased', 'boolean')
|
||||
->columnName('is_campaign_based')
|
||||
->option('default', 1)
|
||||
->build();
|
||||
|
||||
$builder->createField('slotName', 'string')
|
||||
->columnName('slot_name')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
static::addUuidField($builder);
|
||||
self::addProjectsField($builder, 'dynamic_content_projects_xref', 'dynamic_content_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Symfony\Component\Validator\Exception\ConstraintDefinitionException
|
||||
* @throws \Symfony\Component\Validator\Exception\InvalidOptionsException
|
||||
* @throws \Symfony\Component\Validator\Exception\MissingOptionsException
|
||||
*/
|
||||
public static function loadValidatorMetaData(ClassMetadata $metadata): void
|
||||
{
|
||||
$metadata->addPropertyConstraint('name', new NotBlank(['message' => 'mautic.core.name.required']));
|
||||
$metadata->addPropertyConstraint('content', new NoNesting());
|
||||
|
||||
$metadata->addPropertyConstraint('type', new NotBlank(['message' => 'mautic.core.type.required']));
|
||||
$metadata->addPropertyConstraint('type', new Choice(['choices' => (new TypeList())->getChoices()]));
|
||||
|
||||
$metadata->addConstraint(new SlotNameType());
|
||||
|
||||
$metadata->addConstraint(new Callback(
|
||||
function (self $dwc, ExecutionContextInterface $context): void {
|
||||
if (!$dwc->getIsCampaignBased()) {
|
||||
$validator = $context->getValidator();
|
||||
$violations = $validator->validate(
|
||||
$dwc->getSlotName(),
|
||||
[
|
||||
new NotBlank(
|
||||
[
|
||||
'message' => 'mautic.dynamicContent.slot_name.notblank',
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
foreach ($violations as $violation) {
|
||||
$context->buildViolation($violation->getMessage())
|
||||
->atPath('slotName')
|
||||
->addViolation();
|
||||
}
|
||||
$violations = $validator->validate(
|
||||
$dwc->getFilters(),
|
||||
[
|
||||
new Count(
|
||||
[
|
||||
'minMessage' => 'mautic.dynamicContent.filter.options.empty',
|
||||
'min' => 1,
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
foreach ($violations as $violation) {
|
||||
$context->buildViolation($violation->getMessage())
|
||||
->atPath('filters')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('dwc')
|
||||
->addListProperties([
|
||||
'id',
|
||||
'name',
|
||||
'category',
|
||||
'type',
|
||||
])
|
||||
->addProperties([
|
||||
'publishUp',
|
||||
'publishDown',
|
||||
'sentCount',
|
||||
'variantParent',
|
||||
'variantChildren',
|
||||
'content',
|
||||
'utmTags',
|
||||
'filters',
|
||||
'isCampaignBased',
|
||||
'slotName',
|
||||
])
|
||||
->setMaxDepth(1, 'variantParent')
|
||||
->setMaxDepth(1, 'variantChildren')
|
||||
->build();
|
||||
|
||||
self::addProjectsInLoadApiMetadata($metadata, 'dwc');
|
||||
}
|
||||
|
||||
protected function isChanged($prop, $val)
|
||||
{
|
||||
$getter = 'get'.ucfirst($prop);
|
||||
$current = $this->$getter();
|
||||
|
||||
if ('variantParent' == $prop || 'translationParent' == $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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->isChanged('name', $name);
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(string $type): void
|
||||
{
|
||||
$type = strtolower($type);
|
||||
$this->isChanged('type', $type);
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Category
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCategory($category)
|
||||
{
|
||||
$this->isChanged('category', $category);
|
||||
$this->category = $category;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishUp()
|
||||
{
|
||||
return $this->publishUp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $publishUp
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPublishUp($publishUp)
|
||||
{
|
||||
$this->isChanged('publishUp', $publishUp);
|
||||
$this->publishUp = $publishUp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishDown()
|
||||
{
|
||||
return $this->publishDown;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $publishDown
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPublishDown($publishDown)
|
||||
{
|
||||
$this->isChanged('publishDown', $publishDown);
|
||||
$this->publishDown = $publishDown;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->isChanged('content', $content);
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includeVariants
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSentCount($includeVariants = false)
|
||||
{
|
||||
return $includeVariants ? $this->getAccumulativeTranslationCount('getSentCount') : $this->sentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setSentCount($sentCount)
|
||||
{
|
||||
$this->sentCount = $sentCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection
|
||||
*/
|
||||
public function getStats()
|
||||
{
|
||||
return $this->stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsCampaignBased()
|
||||
{
|
||||
return $this->isCampaignBased;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isCampaignBased
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsCampaignBased($isCampaignBased)
|
||||
{
|
||||
$this->isChanged('isCampaignBased', $isCampaignBased);
|
||||
$this->isCampaignBased = $isCampaignBased;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSlotName()
|
||||
{
|
||||
return $this->slotName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slotName
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSlotName($slotName)
|
||||
{
|
||||
$this->isChanged('slotName', $slotName);
|
||||
$this->slotName = $slotName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle callback to clear the slot name if is_campaign is true.
|
||||
*/
|
||||
public function cleanSlotName(): void
|
||||
{
|
||||
if ($this->getIsCampaignBased()) {
|
||||
$this->setSlotName('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DynamicContent
|
||||
*/
|
||||
public function setUtmTags(array $utmTags)
|
||||
{
|
||||
$this->isChanged('utmTags', $utmTags);
|
||||
$this->utmTags = $utmTags;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getUtmTags()
|
||||
{
|
||||
return $this->utmTags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\DynamicContentBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\CommonEntity;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
|
||||
class DynamicContentLeadData extends CommonEntity
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateAdded;
|
||||
|
||||
/**
|
||||
* @var DynamicContent|null
|
||||
*/
|
||||
private $dynamicContent;
|
||||
|
||||
/**
|
||||
* @var Lead
|
||||
*/
|
||||
private $lead;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dataAdded;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $slot;
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('dynamic_content_lead_data')
|
||||
->setCustomRepositoryClass(DynamicContentLeadDataRepository::class);
|
||||
|
||||
$builder->addIdColumns(false, false);
|
||||
|
||||
$builder->addDateAdded(true);
|
||||
|
||||
$builder->addLead();
|
||||
|
||||
$builder->createManyToOne('dynamicContent', 'DynamicContent')
|
||||
->inversedBy('id')
|
||||
->addJoinColumn('dynamic_content_id', 'id', true, false, 'CASCADE')
|
||||
->build();
|
||||
|
||||
$builder->createField('slot', 'text')
|
||||
->columnName('slot')
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateAdded()
|
||||
{
|
||||
return $this->dateAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dateAdded
|
||||
*
|
||||
* @return DynamicContentLeadData
|
||||
*/
|
||||
public function setDateAdded($dateAdded)
|
||||
{
|
||||
$this->dateAdded = $dateAdded;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DynamicContent
|
||||
*/
|
||||
public function getDynamicContent()
|
||||
{
|
||||
return $this->dynamicContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DynamicContent $dynamicContent
|
||||
*
|
||||
* @return DynamicContentLeadData
|
||||
*/
|
||||
public function setDynamicContent($dynamicContent)
|
||||
{
|
||||
$this->dynamicContent = $dynamicContent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Lead
|
||||
*/
|
||||
public function getLead()
|
||||
{
|
||||
return $this->lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Lead $lead
|
||||
*
|
||||
* @return DynamicContentLeadData
|
||||
*/
|
||||
public function setLead($lead)
|
||||
{
|
||||
$this->lead = $lead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDataAdded()
|
||||
{
|
||||
return $this->dataAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dataAdded
|
||||
*
|
||||
* @return DynamicContentLeadData
|
||||
*/
|
||||
public function setDataAdded($dataAdded)
|
||||
{
|
||||
$this->dataAdded = $dataAdded;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSlot()
|
||||
{
|
||||
return $this->slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slot
|
||||
*
|
||||
* @return DynamicContentLeadData
|
||||
*/
|
||||
public function setSlot($slot)
|
||||
{
|
||||
$this->slot = $slot;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\DynamicContentBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<DynamicContentLeadData>
|
||||
*/
|
||||
class DynamicContentLeadDataRepository extends CommonRepository
|
||||
{
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 'dcld';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\DynamicContentBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\CoreBundle\Helper\Serializer;
|
||||
use Mautic\ProjectBundle\Entity\ProjectRepositoryTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<DynamicContent>
|
||||
*/
|
||||
class DynamicContentRepository extends CommonRepository
|
||||
{
|
||||
use ProjectRepositoryTrait;
|
||||
|
||||
/**
|
||||
* Get a list of entities.
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
public function getEntities(array $args = [])
|
||||
{
|
||||
$q = $this->_em
|
||||
->createQueryBuilder()
|
||||
->select('e')
|
||||
->from(DynamicContent::class, 'e', 'e.id');
|
||||
|
||||
if (empty($args['iterable_mode'])) {
|
||||
$q->leftJoin('e.category', 'c');
|
||||
}
|
||||
|
||||
$args['qb'] = $q;
|
||||
|
||||
return parent::getEntities($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\QueryBuilder|\Doctrine\DBAL\Query\QueryBuilder $q
|
||||
*/
|
||||
protected function addSearchCommandWhereClause($q, $filter): array
|
||||
{
|
||||
[$expr, $parameters] = $this->addStandardSearchCommandWhereClause($q, $filter);
|
||||
if ($expr) {
|
||||
return [$expr, $parameters];
|
||||
}
|
||||
|
||||
[$expr, $parameters] = parent::addSearchCommandWhereClause($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.core.searchcommand.lang'):
|
||||
$langUnique = $this->generateRandomParameterName();
|
||||
$langValue = $filter->string.'_%';
|
||||
$forceParameters = [
|
||||
$langUnique => $langValue,
|
||||
$unique => $filter->string,
|
||||
];
|
||||
$expr = $q->expr()->or(
|
||||
$q->expr()->eq('e.language', ":$unique"),
|
||||
$q->expr()->like('e.language', ":$langUnique")
|
||||
);
|
||||
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(),
|
||||
'dynamic_content_id',
|
||||
'dynamic_content_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.core.searchcommand.category',
|
||||
'mautic.core.searchcommand.lang',
|
||||
'mautic.project.searchcommand.name',
|
||||
];
|
||||
|
||||
return array_merge($commands, parent::getSearchCommands());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array<string>>
|
||||
*/
|
||||
protected function getDefaultOrder(): array
|
||||
{
|
||||
return [
|
||||
['e.name', 'ASC'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 'e';
|
||||
}
|
||||
|
||||
/**
|
||||
* Up the sent counts.
|
||||
*
|
||||
* @param int $increaseBy
|
||||
*/
|
||||
public function upSentCount($id, $increaseBy = 1): void
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'dynamic_content')
|
||||
->set('sent_count', 'sent_count + '.(int) $increaseBy)
|
||||
->where('id = '.(int) $id);
|
||||
|
||||
$q->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @param int $limit
|
||||
* @param int $start
|
||||
* @param bool $viewOther
|
||||
* @param bool $topLevel
|
||||
* @param array $ignoreIds
|
||||
* @param string $where
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDynamicContentList($search = '', $limit = 10, $start = 0, $viewOther = false, $topLevel = false, $ignoreIds = [], $where = null)
|
||||
{
|
||||
$q = $this->createQueryBuilder('e');
|
||||
$q->select('partial e.{id, name, language}');
|
||||
|
||||
if (!empty($search)) {
|
||||
if (is_array($search)) {
|
||||
$search = array_map('intval', $search);
|
||||
$q->andWhere($q->expr()->in('e.id', ':search'))
|
||||
->setParameter('search', $search);
|
||||
} else {
|
||||
$q->andWhere($q->expr()->like('e.name', ':search'))
|
||||
->setParameter('search', "%{$search}%");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$viewOther) {
|
||||
$q->andWhere($q->expr()->eq('e.createdBy', ':id'))
|
||||
->setParameter('id', $this->currentUser->getId());
|
||||
}
|
||||
|
||||
if ('translation' == $topLevel) {
|
||||
// only get top level pages
|
||||
$q->andWhere($q->expr()->isNull('e.translationParent'));
|
||||
} elseif ('variant' == $topLevel) {
|
||||
$q->andWhere($q->expr()->isNull('e.variantParent'));
|
||||
}
|
||||
|
||||
if (!empty($ignoreIds)) {
|
||||
$q->andWhere($q->expr()->notIn('e.id', ':dwc_ids'))
|
||||
->setParameter('dwc_ids', $ignoreIds);
|
||||
}
|
||||
|
||||
if ($where) {
|
||||
$q->andWhere($where);
|
||||
}
|
||||
|
||||
$q->orderBy('e.name');
|
||||
|
||||
if (!empty($limit)) {
|
||||
$q->setFirstResult($start)
|
||||
->setMaxResults($limit);
|
||||
}
|
||||
|
||||
return $q->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|object|null
|
||||
*/
|
||||
public function getDynamicContentForSlotFromCampaign($slot)
|
||||
{
|
||||
$qb = $this->_em->getConnection()->createQueryBuilder();
|
||||
|
||||
$qb->select('ce.properties')
|
||||
->from(MAUTIC_TABLE_PREFIX.'campaign_events', 'ce')
|
||||
->leftJoin('ce', MAUTIC_TABLE_PREFIX.'campaigns', 'c', 'c.id = ce.campaign_id')
|
||||
->andWhere($qb->expr()->eq('ce.type', $qb->expr()->literal('dwc.decision')))
|
||||
->andWhere($qb->expr()->like('ce.properties', ':slot'))
|
||||
->setParameter('slot', '%'.$slot.'%')
|
||||
->orderBy('c.is_published');
|
||||
|
||||
$result = $qb->executeQuery()->fetchAllAssociative();
|
||||
|
||||
foreach ($result as $item) {
|
||||
$properties = Serializer::decode($item['properties']);
|
||||
|
||||
if (isset($properties['dynamicContent'])) {
|
||||
$dwc = $this->getEntity($properties['dynamicContent']);
|
||||
|
||||
if ($dwc instanceof DynamicContent) {
|
||||
return $dwc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\DynamicContentBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
|
||||
class Stat
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var DynamicContent|null
|
||||
*/
|
||||
private $dynamicContent;
|
||||
|
||||
/**
|
||||
* @var Lead|null
|
||||
*/
|
||||
private $lead;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateSent;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $sentCount;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $lastSent;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $sentDetails = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $source;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $sourceId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $tokens = [];
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('dynamic_content_stats')
|
||||
->setCustomRepositoryClass(StatRepository::class)
|
||||
->addIndex(['dynamic_content_id', 'lead_id'], 'stat_dynamic_content_search')
|
||||
->addIndex(['source', 'source_id'], 'stat_dynamic_content_source_search')
|
||||
->addIndex(['date_sent'], 'stat_dynamic_content_date_sent');
|
||||
|
||||
$builder->addBigIntIdField();
|
||||
|
||||
$builder->createManyToOne('dynamicContent', 'DynamicContent')
|
||||
->inversedBy('stats')
|
||||
->addJoinColumn('dynamic_content_id', 'id', true, false, 'SET NULL')
|
||||
->build();
|
||||
|
||||
$builder->addLead(true, 'SET NULL');
|
||||
|
||||
$builder->createField('dateSent', 'datetime')
|
||||
->columnName('date_sent')
|
||||
->build();
|
||||
|
||||
$builder->createField('source', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('sourceId', 'integer')
|
||||
->columnName('source_id')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('tokens', 'array')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('sentCount', 'integer', 'sent_count');
|
||||
|
||||
$builder->addNullableField('lastSent', 'datetime', 'last_sent');
|
||||
|
||||
$builder->addNullableField('sentDetails', 'array', 'sent_details');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('stat')
|
||||
->addProperties(
|
||||
[
|
||||
'id',
|
||||
'dateSent',
|
||||
'source',
|
||||
'sentCount',
|
||||
'lastSent',
|
||||
'sourceId',
|
||||
'lead',
|
||||
'dynamicContent',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
public function addSentDetails($details): void
|
||||
{
|
||||
$this->sentDetails[] = $details;
|
||||
|
||||
++$this->sentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Up the sent count.
|
||||
*
|
||||
* @return Stat
|
||||
*/
|
||||
public function upSentCount()
|
||||
{
|
||||
$count = (int) $this->sentCount + 1;
|
||||
$this->sentCount = $count;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*/
|
||||
public function setId($id): void
|
||||
{
|
||||
$this->id = (string) $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DynamicContent
|
||||
*/
|
||||
public function getDynamicContent()
|
||||
{
|
||||
return $this->dynamicContent;
|
||||
}
|
||||
|
||||
public function setDynamicContent(DynamicContent $dynamicContent): void
|
||||
{
|
||||
$this->dynamicContent = $dynamicContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Lead
|
||||
*/
|
||||
public function getLead()
|
||||
{
|
||||
return $this->lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Lead $lead
|
||||
*/
|
||||
public function setLead($lead): void
|
||||
{
|
||||
$this->lead = $lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateSent()
|
||||
{
|
||||
return $this->dateSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dateSent
|
||||
*/
|
||||
public function setDateSent($dateSent): void
|
||||
{
|
||||
$this->dateSent = $dateSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSentCount()
|
||||
{
|
||||
return $this->sentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sentCount
|
||||
*/
|
||||
public function setSentCount($sentCount): void
|
||||
{
|
||||
$this->sentCount = $sentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastSent()
|
||||
{
|
||||
return $this->lastSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lastSent
|
||||
*/
|
||||
public function setLastSent($lastSent): void
|
||||
{
|
||||
$this->lastSent = $lastSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSentDetails()
|
||||
{
|
||||
return $this->sentDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $sentDetails
|
||||
*/
|
||||
public function setSentDetails($sentDetails): void
|
||||
{
|
||||
$this->sentDetails = $sentDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
*/
|
||||
public function setSource($source): void
|
||||
{
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSourceId()
|
||||
{
|
||||
return $this->sourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sourceId
|
||||
*/
|
||||
public function setSourceId($sourceId): void
|
||||
{
|
||||
$this->sourceId = $sourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTokens()
|
||||
{
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tokens
|
||||
*/
|
||||
public function setTokens($tokens): void
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\DynamicContentBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\LeadBundle\Entity\TimelineTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Stat>
|
||||
*/
|
||||
class StatRepository extends CommonRepository
|
||||
{
|
||||
use TimelineTrait;
|
||||
|
||||
public function getSentStats($dynamicContentId): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('s.lead_id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'dynamic_content_stats', 's')
|
||||
->where('s.dynamic_content_id = :dynamic_content')
|
||||
->setParameter('dynamic_content', $dynamicContentId);
|
||||
|
||||
$result = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
// index by lead
|
||||
$stats = [];
|
||||
|
||||
foreach ($result as $r) {
|
||||
$stats[$r['lead_id']] = $r['lead_id'];
|
||||
}
|
||||
|
||||
unset($result);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|array $dynamicContentIds
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSentCount($dynamicContentIds = null)
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
|
||||
$q->select('count(s.id) as sent_count')
|
||||
->from(MAUTIC_TABLE_PREFIX.'dynamic_content_stats', 's');
|
||||
|
||||
if ($dynamicContentIds) {
|
||||
if (!is_array($dynamicContentIds)) {
|
||||
$dynamicContentIds = [(int) $dynamicContentIds];
|
||||
}
|
||||
$q->where(
|
||||
$q->expr()->in('s.dynamic_content_id', $dynamicContentIds)
|
||||
);
|
||||
}
|
||||
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
return (isset($results[0])) ? $results[0]['sent_count'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sent counts based grouped by dynamic content Id.
|
||||
*
|
||||
* @param array $dynamicContentIds
|
||||
*/
|
||||
public function getSentCounts($dynamicContentIds = [], ?\DateTime $fromDate = null): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('s.dynamic_content_id, count(s.id) as sent_count')
|
||||
->from(MAUTIC_TABLE_PREFIX.'dynamic_content_stats', 's')
|
||||
->andWhere(
|
||||
$q->expr()->in('e.dynamic_content_id', $dynamicContentIds)
|
||||
);
|
||||
|
||||
if (null !== $fromDate) {
|
||||
// make sure the date is UTC
|
||||
$dt = new DateTimeHelper($fromDate);
|
||||
$q->andWhere(
|
||||
$q->expr()->gte('e.date_sent', $q->expr()->literal($dt->toUtcString()))
|
||||
);
|
||||
}
|
||||
$q->groupBy('e.dynamic_content_id');
|
||||
|
||||
// get a total number of sent DC stats first
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
$counts = [];
|
||||
|
||||
foreach ($results as $r) {
|
||||
$counts[$r['dynamic_content_id']] = $r['sent_count'];
|
||||
}
|
||||
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lead's dynamic content stat.
|
||||
*
|
||||
* @param int|null $leadId
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getLeadStats($leadId = null, array $options = [])
|
||||
{
|
||||
$query = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
|
||||
$query->select('dc.id AS dynamic_content_id, s.id, s.date_sent as dateSent, dc.name, s.sent_details as sentDetails, s.lead_id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'dynamic_content_stats', 's')
|
||||
->leftJoin('s', MAUTIC_TABLE_PREFIX.'dynamic_content', 'dc', 'dc.id = s.dynamic_content_id');
|
||||
|
||||
if ($leadId) {
|
||||
$query->where('s.lead_id = :leadId')
|
||||
->setParameter('leadId', $leadId);
|
||||
}
|
||||
|
||||
if (isset($options['search']) && $options['search']) {
|
||||
$query->andWhere('dc.name LIKE :search')
|
||||
->setParameter('search', '%'.$options['search'].'%');
|
||||
}
|
||||
|
||||
return $this->getTimelineResults($query, $options, 'dc.name', 's.date_sent', ['sentDetails'], ['dateSent'], null, 's.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.'dynamic_content_stats')
|
||||
->set('lead_id', (int) $toLeadId)
|
||||
->where('lead_id = '.(int) $fromLeadId)
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a stat.
|
||||
*/
|
||||
public function deleteStat($id): void
|
||||
{
|
||||
$this->_em->getConnection()->delete(MAUTIC_TABLE_PREFIX.'dynamic_content_stats', ['id' => (int) $id]);
|
||||
}
|
||||
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 's';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user