Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class AbstractReportEvent extends Event
|
||||
{
|
||||
protected ?string $context = null;
|
||||
|
||||
/**
|
||||
* Report entity.
|
||||
*
|
||||
* @var Report
|
||||
*/
|
||||
protected $report;
|
||||
|
||||
/**
|
||||
* @return Report
|
||||
*/
|
||||
public function getReport()
|
||||
{
|
||||
return $this->report;
|
||||
}
|
||||
|
||||
public function getContext(): ?string
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function checkContext($context)
|
||||
{
|
||||
if (empty($this->context)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_array($context)) {
|
||||
$res = array_filter($context, fn ($elem) => 0 === stripos($this->context, (string) $elem));
|
||||
|
||||
return count($res) > 0;
|
||||
} elseif ($this->context == $context) {
|
||||
return true;
|
||||
} elseif (0 === stripos($this->context, (string) $context)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
final class ColumnCollectEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private array $columns;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $properties
|
||||
*/
|
||||
public function __construct(
|
||||
private string $object,
|
||||
private array $properties = [],
|
||||
) {
|
||||
$this->columns = [];
|
||||
}
|
||||
|
||||
public function getObject(): string
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getProperties(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, mixed>> $column
|
||||
*/
|
||||
public function addColumns(array $column): void
|
||||
{
|
||||
$this->columns = array_merge($this->columns, $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public function getColumns(): array
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
class PermanentReportFileCreatedEvent extends AbstractReportEvent
|
||||
{
|
||||
public function __construct(Report $report)
|
||||
{
|
||||
$this->report = $report;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
use Mautic\ChannelBundle\Helper\ChannelListHelper;
|
||||
use Mautic\ReportBundle\Builder\MauticReportBuilder;
|
||||
use Mautic\ReportBundle\Helper\ReportHelper;
|
||||
use Mautic\ReportBundle\Model\ReportModel;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ReportBuilderEvent extends AbstractReportEvent
|
||||
{
|
||||
/**
|
||||
* Container with registered tables and columns.
|
||||
*/
|
||||
private array $tableArray = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $supportedGraphs = [
|
||||
'table',
|
||||
'bar',
|
||||
'pie',
|
||||
'line',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private array $graphArray = [];
|
||||
|
||||
/**
|
||||
* @param mixed[]|Paginator|array $leadFields list of published array of lead fields
|
||||
*/
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
private ChannelListHelper $channelListHelper,
|
||||
string $context,
|
||||
private array|Paginator $leadFields,
|
||||
private ReportHelper $reportHelper,
|
||||
private ?string $reportSource = null,
|
||||
) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a table with the specified columns to the lookup.
|
||||
*
|
||||
* The data should be an associative array with the following data:
|
||||
* 'display_name' => The translation key to display in the select list
|
||||
* 'columns' => An array containing the table's columns
|
||||
*
|
||||
* @param string $context Context for data
|
||||
* @param array $data Data array for the table
|
||||
*
|
||||
* @return ReportBuilderEvent
|
||||
*/
|
||||
public function addTable($context, array $data, $group = null)
|
||||
{
|
||||
$data['group'] = (null == $group) ? $context : $group;
|
||||
|
||||
foreach ($data['columns'] as $column => &$d) {
|
||||
$d['label'] = null !== $d['label'] ? $this->translator->trans($d['label']) : '';
|
||||
if (!isset($d['alias'])) {
|
||||
$d['alias'] = substr(
|
||||
$column,
|
||||
false !== ($pos = strpos($column, '.')) ? $pos + 1 : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uasort($data['columns'], fn ($a, $b) => strnatcmp((string) $a['label'], (string) $b['label']));
|
||||
|
||||
if (isset($data['filters'])) {
|
||||
foreach ($data['filters'] as $column => &$d) {
|
||||
$d['label'] = $this->translator->trans($d['label']);
|
||||
if (!isset($d['alias'])) {
|
||||
$d['alias'] = substr(
|
||||
$column,
|
||||
false !== ($pos = strpos($column, '.')) ? $pos + 1 : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uasort($data['filters'], fn ($a, $b) => strnatcmp((string) $a['label'], (string) $b['label']));
|
||||
}
|
||||
|
||||
$this->tableArray[$context] = $data;
|
||||
|
||||
if ($this->context == $context) {
|
||||
$this->stopPropagation();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the tables in the lookup array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
return $this->tableArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the source of the report.
|
||||
*/
|
||||
public function getReportSource(): ?string
|
||||
{
|
||||
return $this->reportSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns standard form fields such as id, name, publish_up, etc.
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return array<string,array<string,string>>
|
||||
*/
|
||||
public function getStandardColumns($prefix, $removeColumns = [], $idLink = null): array
|
||||
{
|
||||
return $this->reportHelper->getStandardColumns($prefix, $removeColumns, (string) $idLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lead columns.
|
||||
*/
|
||||
public function getLeadColumns($prefix = 'l.'): array
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
foreach ($this->leadFields as $fieldArray) {
|
||||
$fields[$prefix.$fieldArray['alias']] = [
|
||||
'label' => $this->translator->trans('mautic.report.field.lead.label', ['%field%' => $fieldArray['label']]),
|
||||
'type' => $this->reportHelper->getReportBuilderFieldType($fieldArray['type']),
|
||||
'alias' => $fieldArray['alias'],
|
||||
];
|
||||
}
|
||||
$fields[$prefix.'id'] = [
|
||||
'label' => 'mautic.report.field.lead.id',
|
||||
'type' => 'int',
|
||||
'link' => 'mautic_contact_action',
|
||||
'alias' => 'contactId',
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IP Address column.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function getIpColumn($prefix = 'i.'): array
|
||||
{
|
||||
return [
|
||||
$prefix.'ip_address' => [
|
||||
'label' => 'mautic.core.ipaddress',
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add category columns.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function getCategoryColumns($prefix = 'c.'): array
|
||||
{
|
||||
return [
|
||||
$prefix.'id' => [
|
||||
'label' => 'mautic.report.field.category_id',
|
||||
'type' => 'int',
|
||||
'alias' => 'category_id',
|
||||
],
|
||||
$prefix.'title' => [
|
||||
'label' => 'mautic.report.field.category_name',
|
||||
'type' => 'string',
|
||||
'alias' => 'category_title',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add campaign columns joined by the campaign lead event log table.
|
||||
*/
|
||||
public function getCampaignByChannelColumns(): array
|
||||
{
|
||||
return [
|
||||
'clel.campaign_id' => [
|
||||
'label' => 'mautic.campaign.campaign.id',
|
||||
'type' => 'string',
|
||||
],
|
||||
'cmp.name' => [
|
||||
'label' => 'mautic.campaign.campaign',
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<MauticReportBuilder::*, mixed[]>
|
||||
*/
|
||||
public function getChannelColumns(): array
|
||||
{
|
||||
$channelColumns = [
|
||||
MauticReportBuilder::CHANNEL_COLUMN_CATEGORY_ID => [
|
||||
'label' => 'mautic.report.campaign.channel.category_id',
|
||||
'type' => 'int',
|
||||
'alias' => 'channel_category_id',
|
||||
'channelData' => [],
|
||||
],
|
||||
MauticReportBuilder::CHANNEL_COLUMN_CREATED_BY => [
|
||||
'label' => 'mautic.report.campaign.channel.created_by',
|
||||
'type' => 'int',
|
||||
'alias' => 'channel_created_by',
|
||||
'channelData' => [],
|
||||
],
|
||||
MauticReportBuilder::CHANNEL_COLUMN_CREATED_BY_USER => [
|
||||
'label' => 'mautic.report.campaign.channel.created_by_user',
|
||||
'type' => 'string',
|
||||
'alias' => 'channel_created_by_user',
|
||||
'channelData' => [],
|
||||
],
|
||||
MauticReportBuilder::CHANNEL_COLUMN_DATE_ADDED => [
|
||||
'label' => 'mautic.report.campaign.channel.date_added',
|
||||
'type' => 'datetime',
|
||||
'alias' => 'channel_date_added',
|
||||
'channelData' => [],
|
||||
],
|
||||
MauticReportBuilder::CHANNEL_COLUMN_DESCRIPTION => [
|
||||
'label' => 'mautic.report.campaign.channel.description',
|
||||
'type' => 'string',
|
||||
'alias' => 'channel_description',
|
||||
'channelData' => [],
|
||||
],
|
||||
MauticReportBuilder::CHANNEL_COLUMN_NAME => [
|
||||
'label' => 'mautic.report.campaign.channel.name',
|
||||
'type' => 'string',
|
||||
'alias' => 'channel_name',
|
||||
'channelData' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->channelListHelper->getChannels() as $channel => $details) {
|
||||
if (!array_key_exists(ReportModel::CHANNEL_FEATURE, $details)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reportDetails = $details[ReportModel::CHANNEL_FEATURE];
|
||||
|
||||
$hasFields = array_key_exists('fields', $reportDetails) && is_array($reportDetails['fields']);
|
||||
|
||||
foreach ($channelColumns as $column => $definition) {
|
||||
$channelColumnName = $hasFields && array_key_exists($column, $reportDetails['fields'])
|
||||
? $reportDetails['fields'][$column]
|
||||
: str_replace('channel.', $channel.'.', $column);
|
||||
|
||||
$channelColumns[$column]['channelData'][$channel] = [
|
||||
'prefix' => $channel,
|
||||
'column' => $channelColumnName,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $channelColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addGraph($context, $type, $graphId, $options = [])
|
||||
{
|
||||
if (in_array($type, $this->supportedGraphs)) {
|
||||
$this->graphArray[$context][$graphId] = [
|
||||
'options' => $options,
|
||||
'type' => $type,
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get graphs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGraphs()
|
||||
{
|
||||
return $this->graphArray;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
class ReportDataEvent extends AbstractReportEvent
|
||||
{
|
||||
private int $totalResults;
|
||||
|
||||
public function __construct(
|
||||
Report $report,
|
||||
private array $data,
|
||||
$totalResults,
|
||||
private array $options,
|
||||
) {
|
||||
$this->context = $report->getSource();
|
||||
$this->report = $report;
|
||||
$this->totalResults = (int) $totalResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function getTotalResults(): int
|
||||
{
|
||||
return $this->totalResults;
|
||||
}
|
||||
|
||||
public function updateColumnType(string $alias, string $type): void
|
||||
{
|
||||
foreach ($this->options['columns'] as &$column) {
|
||||
if ($column['alias'] === $alias) {
|
||||
$column['type'] = $type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Mautic\CoreBundle\Event\CommonEvent;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
class ReportEvent extends CommonEvent
|
||||
{
|
||||
/**
|
||||
* @param bool $isNew
|
||||
*/
|
||||
public function __construct(Report $report, $isNew = false)
|
||||
{
|
||||
$this->entity = $report;
|
||||
$this->isNew = $isNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Report entity.
|
||||
*
|
||||
* @return Report
|
||||
*/
|
||||
public function getReport()
|
||||
{
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Report entity.
|
||||
*/
|
||||
public function setReport(Report $report): void
|
||||
{
|
||||
$this->entity = $report;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use Mautic\ChannelBundle\Helper\ChannelListHelper;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Mautic\ReportBundle\Model\ReportModel;
|
||||
|
||||
class ReportGeneratorEvent extends AbstractReportEvent
|
||||
{
|
||||
public const CATEGORY_PREFIX = 'c';
|
||||
|
||||
public const CONTACT_PREFIX = 'l';
|
||||
|
||||
public const COMPANY_PREFIX = 'comp';
|
||||
|
||||
public const COMPANY_LEAD_PREFIX = 'companies_lead';
|
||||
|
||||
public const IP_ADDRESS_PREFIX = 'i';
|
||||
|
||||
private array $selectColumns = [];
|
||||
|
||||
private ?string $contentTemplate = null;
|
||||
|
||||
private ?ExpressionBuilder $filterExpression = null;
|
||||
|
||||
private ?array $sortedFilters = null;
|
||||
|
||||
public function __construct(
|
||||
Report $report,
|
||||
private array $options,
|
||||
private QueryBuilder $queryBuilder,
|
||||
private ChannelListHelper $channelListHelper,
|
||||
) {
|
||||
$this->report = $report;
|
||||
$this->context = $report->getSource();
|
||||
}
|
||||
|
||||
public function getQueryBuilder(): QueryBuilder
|
||||
{
|
||||
return $this->queryBuilder;
|
||||
}
|
||||
|
||||
public function setQueryBuilder(QueryBuilder $queryBuilder): self
|
||||
{
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContentTemplate(): ?string
|
||||
{
|
||||
if ($this->contentTemplate) {
|
||||
return $this->contentTemplate;
|
||||
}
|
||||
|
||||
// Default content template
|
||||
return '@MauticReport/Report/details.html.twig';
|
||||
}
|
||||
|
||||
public function setContentTemplate(?string $contentTemplate): self
|
||||
{
|
||||
$this->contentTemplate = $contentTemplate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSelectColumns()
|
||||
{
|
||||
return $this->selectColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom select columns with aliases based on report settings.
|
||||
*/
|
||||
public function setSelectColumns(array $selectColumns): self
|
||||
{
|
||||
$this->selectColumns = $selectColumns;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFilterExpression(): ?ExpressionBuilder
|
||||
{
|
||||
return $this->filterExpression;
|
||||
}
|
||||
|
||||
public function setFilterExpression(ExpressionBuilder $filterExpression): self
|
||||
{
|
||||
$this->filterExpression = $filterExpression;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add category left join.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $categoryPrefix
|
||||
*/
|
||||
public function addCategoryLeftJoin(QueryBuilder $queryBuilder, $prefix, $categoryPrefix = self::CATEGORY_PREFIX): self
|
||||
{
|
||||
if ($this->usesColumnWithPrefix($categoryPrefix)) {
|
||||
$queryBuilder->leftJoin($prefix, MAUTIC_TABLE_PREFIX.'categories', $categoryPrefix, $categoryPrefix.'.id = '.$prefix.'.category_id');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add lead left join.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $leadPrefix
|
||||
*/
|
||||
public function addLeadLeftJoin(QueryBuilder $queryBuilder, $prefix, $leadPrefix = self::CONTACT_PREFIX): self
|
||||
{
|
||||
if ($this->usesColumnWithPrefix($leadPrefix)
|
||||
|| $this->usesColumnWithPrefix(self::IP_ADDRESS_PREFIX)
|
||||
|| $this->usesColumnWithPrefix(self::COMPANY_PREFIX)
|
||||
|| $this->usesColumn('cmp.name')
|
||||
|| $this->usesColumn('clel.campaign_id')
|
||||
|| $this->usesColumn('dnc_preferences')
|
||||
) {
|
||||
$queryBuilder->leftJoin($prefix, MAUTIC_TABLE_PREFIX.'leads', $leadPrefix, $leadPrefix.'.id = '.$prefix.'.lead_id');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IP left join.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $ipPrefix
|
||||
*/
|
||||
public function addIpAddressLeftJoin(QueryBuilder $queryBuilder, $prefix, $ipPrefix = self::IP_ADDRESS_PREFIX): self
|
||||
{
|
||||
if ($this->usesColumnWithPrefix($ipPrefix)) {
|
||||
$queryBuilder->leftJoin($prefix, MAUTIC_TABLE_PREFIX.'ip_addresses', $ipPrefix, $ipPrefix.'.id = '.$prefix.'.ip_id');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IP left join with lead join.
|
||||
*
|
||||
* @param string $ipXrefPrefix
|
||||
* @param string $ipPrefix
|
||||
* @param string $leadPrefix
|
||||
*/
|
||||
public function addLeadIpAddressLeftJoin(QueryBuilder $queryBuilder, $ipXrefPrefix = 'lip', $ipPrefix = self::IP_ADDRESS_PREFIX, $leadPrefix = self::CONTACT_PREFIX): self
|
||||
{
|
||||
if ($this->usesColumnWithPrefix($ipPrefix)) {
|
||||
$this->addIpAddressLeftJoin($queryBuilder, $ipXrefPrefix, $ipPrefix);
|
||||
$queryBuilder->leftJoin($leadPrefix, MAUTIC_TABLE_PREFIX.'lead_ips_xref', $ipXrefPrefix, $ipXrefPrefix.'.lead_id = '.$leadPrefix.'.id');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IP left join.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $channel
|
||||
* @param string $leadPrefix
|
||||
* @param string $onColumn
|
||||
*/
|
||||
public function addCampaignByChannelJoin(QueryBuilder $queryBuilder, $prefix, $channel, $leadPrefix = self::CONTACT_PREFIX, $onColumn = 'id'): self
|
||||
{
|
||||
if ($this->usesColumn('cmp.name') || $this->usesColumn('clel.campaign_id')) {
|
||||
$condition = "clel.channel='{$channel}' AND {$prefix}.{$onColumn} = clel.channel_id AND clel.lead_id = {$leadPrefix}.id";
|
||||
$queryBuilder->leftJoin($prefix, MAUTIC_TABLE_PREFIX.'campaign_lead_event_log', 'clel', $condition);
|
||||
$queryBuilder->leftJoin('clel', MAUTIC_TABLE_PREFIX.'campaigns', 'cmp', 'cmp.id = clel.campaign_id');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join channel columns.
|
||||
*
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function addChannelLeftJoins(QueryBuilder $queryBuilder, $prefix): self
|
||||
{
|
||||
foreach ($this->channelListHelper->getChannels() as $channel => $details) {
|
||||
if (!array_key_exists(ReportModel::CHANNEL_FEATURE, $details)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reportDetails = $details[ReportModel::CHANNEL_FEATURE];
|
||||
|
||||
if (!array_key_exists('table', $reportDetails)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$channelParameter = 'channelParameter'.$channel;
|
||||
|
||||
$queryBuilder->leftJoin(
|
||||
$prefix,
|
||||
MAUTIC_TABLE_PREFIX.$reportDetails['table'],
|
||||
$channel,
|
||||
$prefix.'.channel_id = '.$channel.'.id AND '.$prefix.'.channel = :'.$channelParameter
|
||||
);
|
||||
|
||||
$queryBuilder->setParameter($channelParameter, $channel);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add company left join.
|
||||
*/
|
||||
public function addCompanyLeftJoin(QueryBuilder $queryBuilder, string $companyPrefix = self::COMPANY_PREFIX, string $contactPrefix = self::CONTACT_PREFIX): void
|
||||
{
|
||||
if ($this->usesColumnWithPrefix($companyPrefix) || $this->usesColumnWithPrefix(self::COMPANY_LEAD_PREFIX)) {
|
||||
if ($this->isJoined($queryBuilder, MAUTIC_TABLE_PREFIX.'companies_leads', 'l', self::COMPANY_LEAD_PREFIX)) {
|
||||
return;
|
||||
}
|
||||
$queryBuilder->leftJoin('l', MAUTIC_TABLE_PREFIX.'companies_leads', self::COMPANY_LEAD_PREFIX, $contactPrefix.'.id ='.self::COMPANY_LEAD_PREFIX.'.lead_id');
|
||||
$queryBuilder->leftJoin(self::COMPANY_LEAD_PREFIX, MAUTIC_TABLE_PREFIX.'companies', $companyPrefix, self::COMPANY_LEAD_PREFIX.'.company_id = '.$companyPrefix.'.id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply date filters to the query.
|
||||
*
|
||||
* @param string $dateColumn
|
||||
* @param string $tablePrefix
|
||||
* @param bool $dateOnly
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function applyDateFilters(QueryBuilder $queryBuilder, $dateColumn, $tablePrefix = 't', $dateOnly = false): ReportGeneratorEvent
|
||||
{
|
||||
$this->setDateRangeQueryFilters(
|
||||
$queryBuilder, $tablePrefix, $dateOnly, $dateColumn,
|
||||
'%1$s IS NULL OR (DATE(%1$s) BETWEEN :dateFrom AND :dateTo)',
|
||||
'%1$s IS NULL OR (%1$s BETWEEN :dateFrom AND :dateTo)'
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function applyDateFiltersWithoutNullValues(QueryBuilder $queryBuilder, string $dateColumn, string $tablePrefix = 't', bool $dateOnly = false): ReportGeneratorEvent
|
||||
{
|
||||
$this->setDateRangeQueryFilters(
|
||||
$queryBuilder, $tablePrefix, $dateOnly, $dateColumn,
|
||||
'DATE(%1$s) BETWEEN :dateFrom AND :dateTo',
|
||||
'%1$s BETWEEN :dateFrom AND :dateTo'
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasColumnWithPrefix(string $prefix): bool
|
||||
{
|
||||
$columns = $this->getReport()->getSelectAndAggregatorAndOrderAndGroupByColumns();
|
||||
$pattern = "/^{$prefix}\./";
|
||||
|
||||
return count(preg_grep($pattern, $columns)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the report uses the column anywhere in the query.
|
||||
*
|
||||
* @param string|array $column
|
||||
*/
|
||||
public function usesColumn($column): bool
|
||||
{
|
||||
return $this->hasColumn($column) || $this->hasFilter($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the report uses the prefix anywhere in the query.
|
||||
*/
|
||||
public function usesColumnWithPrefix(string $prefix): bool
|
||||
{
|
||||
if ($this->hasColumnWithPrefix($prefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->buildSortedFilters();
|
||||
|
||||
$pattern = "/^{$prefix}\./";
|
||||
|
||||
return count(preg_grep($pattern, array_keys($this->sortedFilters))) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the report has a specific column.
|
||||
*
|
||||
* @param array|string $column
|
||||
*/
|
||||
public function hasColumn($column): bool
|
||||
{
|
||||
$columns = $this->getReport()->getSelectAndAggregatorAndOrderAndGroupByColumns();
|
||||
|
||||
if (is_array($column)) {
|
||||
foreach ($column as $checkMe) {
|
||||
if (in_array($checkMe, $columns, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($column, $columns, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the report has a specific filter.
|
||||
*
|
||||
* @param array|string $column
|
||||
*/
|
||||
public function hasFilter($column): bool
|
||||
{
|
||||
$this->buildSortedFilters();
|
||||
|
||||
if (is_array($column)) {
|
||||
foreach ($column as $checkMe) {
|
||||
if (isset($this->sortedFilters[$checkMe])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($this->sortedFilters[$column]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filter value from a specific filter.
|
||||
*
|
||||
* @param string $column
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function getFilterValue($column)
|
||||
{
|
||||
return $this->getReport()->getFilterValue($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filter values from a specific filter.
|
||||
*
|
||||
* @param string $column
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function getFilterValues($column): array
|
||||
{
|
||||
return $this->getReport()->getFilterValues($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the report has a groupBy columns selected.
|
||||
*/
|
||||
public function hasGroupBy(): bool
|
||||
{
|
||||
if (!empty($this->getReport()->getGroupBy())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function createParameterName(): string
|
||||
{
|
||||
$alpha_numeric = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
return substr(str_shuffle($alpha_numeric), 0, 8);
|
||||
}
|
||||
|
||||
private function buildSortedFilters(): void
|
||||
{
|
||||
if (null !== $this->sortedFilters) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sortedFilters = [];
|
||||
$filters = (array) $this->getReport()->getFilters();
|
||||
|
||||
foreach ($filters as $field) {
|
||||
$this->sortedFilters[$field['column']] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function setDateRangeQueryFilters(QueryBuilder $queryBuilder, string $tablePrefix, bool $dateOnly, string $dateColumn, string $dateOnlyFilter, string $dateTimeFilter): void
|
||||
{
|
||||
if ($tablePrefix) {
|
||||
$tablePrefix .= '.';
|
||||
}
|
||||
|
||||
if (empty($this->options['dateFrom'])) {
|
||||
$this->options['dateFrom'] = new \DateTime();
|
||||
$this->options['dateFrom']->modify('-30 days');
|
||||
}
|
||||
|
||||
if (empty($this->options['dateTo'])) {
|
||||
$this->options['dateTo'] = new \DateTime();
|
||||
}
|
||||
|
||||
if ($dateOnly) {
|
||||
$queryBuilder->andWhere(sprintf($dateOnlyFilter, $tablePrefix.$dateColumn));
|
||||
$queryBuilder->setParameter('dateFrom', $this->options['dateFrom']->format('Y-m-d'));
|
||||
$queryBuilder->setParameter('dateTo', $this->options['dateTo']->format('Y-m-d'));
|
||||
} else {
|
||||
$queryBuilder->andWhere(sprintf($dateTimeFilter, $tablePrefix.$dateColumn));
|
||||
$queryBuilder->setParameter('dateFrom', $this->options['dateFrom']->format('Y-m-d H:i:s'));
|
||||
$queryBuilder->setParameter('dateTo', $this->options['dateTo']->format('Y-m-d H:i:s'));
|
||||
}
|
||||
}
|
||||
|
||||
private function isJoined(QueryBuilder $query, string $table, string $fromAlias, string $alias): bool
|
||||
{
|
||||
$queryParts = $query->getQueryParts();
|
||||
$joins = !empty($queryParts) && $queryParts['join'] ? $queryParts['join'] : null;
|
||||
if (empty($joins) || (!empty($joins) && empty($joins[$fromAlias]))) { // @phpstan-ignore-line
|
||||
return false;
|
||||
}
|
||||
foreach ($joins[$fromAlias] as $join) {
|
||||
if ($join['joinTable'] == $table && $join['joinAlias'] == $alias) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
class ReportGraphEvent extends AbstractReportEvent
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $requestedGraphs
|
||||
*/
|
||||
public function __construct(
|
||||
Report $report,
|
||||
private array $requestedGraphs,
|
||||
private QueryBuilder $queryBuilder,
|
||||
) {
|
||||
$this->report = $report;
|
||||
$this->context = $report->getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the graphs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGraphs()
|
||||
{
|
||||
return $this->requestedGraphs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the graph array.
|
||||
*
|
||||
* @param string $graph
|
||||
* @param array $data prepared for this chart
|
||||
*/
|
||||
public function setGraph($graph, $data): void
|
||||
{
|
||||
if (!isset($this->requestedGraphs[$graph]['data'])) {
|
||||
$this->requestedGraphs[$graph]['data'] = [];
|
||||
}
|
||||
$this->requestedGraphs[$graph]['data'] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the options array for the graph.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions($graph)
|
||||
{
|
||||
return $this->requestedGraphs[$graph]['options'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an option for the graph.
|
||||
*
|
||||
* @param string $graph
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
*/
|
||||
public function setOption($graph, $key, $value): void
|
||||
{
|
||||
if (!isset($this->requestedGraphs[$graph]['options'])) {
|
||||
$this->requestedGraphs[$graph]['options'] = [];
|
||||
}
|
||||
$this->requestedGraphs[$graph]['options'][$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the options for a graph.
|
||||
*
|
||||
* @param string $graph
|
||||
* @param array $options
|
||||
*/
|
||||
public function setOptions($graph, $options): void
|
||||
{
|
||||
$this->requestedGraphs[$graph]['options'] = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get graphs that are requested.
|
||||
*/
|
||||
public function getRequestedGraphs(): array
|
||||
{
|
||||
return array_keys($this->requestedGraphs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function getQueryBuilder()
|
||||
{
|
||||
return $this->queryBuilder;
|
||||
}
|
||||
|
||||
public function setQueryBuilder(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
class ReportQueryEvent extends AbstractReportEvent
|
||||
{
|
||||
private int $totalResults;
|
||||
|
||||
public function __construct(
|
||||
Report $report,
|
||||
private QueryBuilder $query,
|
||||
$totalResults,
|
||||
private array $options,
|
||||
) {
|
||||
$this->context = $report->getSource();
|
||||
$this->report = $report;
|
||||
$this->totalResults = (int) $totalResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
*/
|
||||
public function setQuery($query): void
|
||||
{
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function getTotalResults(): int
|
||||
{
|
||||
return $this->totalResults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Event;
|
||||
|
||||
use Mautic\ReportBundle\Entity\Scheduler;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class ReportScheduleSendEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
public function __construct(
|
||||
private Scheduler $scheduler,
|
||||
private $file,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Scheduler
|
||||
*/
|
||||
public function getScheduler()
|
||||
{
|
||||
return $this->scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user