Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Builder;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\NotSupportedScheduleTypeException;
|
||||
use Mautic\ReportBundle\Scheduler\Factory\SchedulerTemplateFactory;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
use Recurr\Exception\InvalidWeekday;
|
||||
use Recurr\Rule;
|
||||
use Recurr\Transformer\ArrayTransformer;
|
||||
|
||||
class SchedulerBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private SchedulerTemplateFactory $schedulerTemplateFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Recurr\Recurrence[]|\Recurr\RecurrenceCollection
|
||||
*
|
||||
* @throws InvalidSchedulerException
|
||||
* @throws NotSupportedScheduleTypeException
|
||||
*/
|
||||
public function getNextEvent(SchedulerInterface $scheduler)
|
||||
{
|
||||
return $this->getNextEvents($scheduler, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $count
|
||||
*
|
||||
* @return \Recurr\RecurrenceCollection
|
||||
*
|
||||
* @throws InvalidSchedulerException
|
||||
* @throws NotSupportedScheduleTypeException
|
||||
*/
|
||||
public function getNextEvents(SchedulerInterface $scheduler, $count)
|
||||
{
|
||||
if (!$scheduler->isScheduled()) {
|
||||
throw new InvalidSchedulerException();
|
||||
}
|
||||
|
||||
$builder = $this->schedulerTemplateFactory->getBuilder($scheduler);
|
||||
$startDate = new \DateTime();
|
||||
$rule = new Rule();
|
||||
|
||||
if (!$scheduler->isScheduledNow()) {
|
||||
$startDate->setTime(0, 0)->modify('+1 day');
|
||||
}
|
||||
|
||||
$rule->setStartDate($startDate)->setCount($count);
|
||||
|
||||
try {
|
||||
$finalScheduler = $builder->build($rule, $scheduler);
|
||||
$transformer = new ArrayTransformer();
|
||||
|
||||
return $transformer->transform($finalScheduler);
|
||||
} catch (InvalidWeekday) {
|
||||
throw new InvalidSchedulerException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Builder;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\BuilderInterface;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
use Recurr\Exception\InvalidArgument;
|
||||
use Recurr\Rule;
|
||||
|
||||
class SchedulerDailyBuilder implements BuilderInterface
|
||||
{
|
||||
/**
|
||||
* @throws InvalidSchedulerException
|
||||
*/
|
||||
public function build(Rule $rule, SchedulerInterface $scheduler): Rule
|
||||
{
|
||||
try {
|
||||
$rule->setFreq('DAILY');
|
||||
} catch (InvalidArgument) {
|
||||
throw new InvalidSchedulerException();
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Builder;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\BuilderInterface;
|
||||
use Mautic\ReportBundle\Scheduler\Enum\SchedulerEnum;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
use Recurr\Exception\InvalidArgument;
|
||||
use Recurr\Exception\InvalidRRule;
|
||||
use Recurr\Rule;
|
||||
|
||||
class SchedulerMonthBuilder implements BuilderInterface
|
||||
{
|
||||
/**
|
||||
* @throws InvalidSchedulerException
|
||||
*/
|
||||
public function build(Rule $rule, SchedulerInterface $scheduler): Rule
|
||||
{
|
||||
try {
|
||||
$frequency = $scheduler->getScheduleMonthFrequency();
|
||||
|
||||
$rule->setFreq('MONTHLY');
|
||||
|
||||
if ($scheduler->isScheduledWeekDays()) {
|
||||
$days = SchedulerEnum::getWeekDays();
|
||||
} else {
|
||||
$days = [$scheduler->getScheduleDay()];
|
||||
}
|
||||
|
||||
foreach ($days as $key => $day) {
|
||||
$days[$key] = $frequency.$day;
|
||||
}
|
||||
|
||||
$rule->setByDay($days);
|
||||
} catch (InvalidArgument|InvalidRRule) {
|
||||
throw new InvalidSchedulerException();
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Builder;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\BuilderInterface;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
use Recurr\Exception\InvalidArgument;
|
||||
use Recurr\Rule;
|
||||
|
||||
class SchedulerNowBuilder implements BuilderInterface
|
||||
{
|
||||
/**
|
||||
* @throws InvalidSchedulerException
|
||||
*/
|
||||
public function build(Rule $rule, SchedulerInterface $scheduler): Rule
|
||||
{
|
||||
try {
|
||||
$rule->setFreq('SECONDLY');
|
||||
} catch (InvalidArgument) {
|
||||
throw new InvalidSchedulerException();
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Builder;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\BuilderInterface;
|
||||
use Mautic\ReportBundle\Scheduler\Enum\SchedulerEnum;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
use Recurr\Exception\InvalidArgument;
|
||||
use Recurr\Exception\InvalidRRule;
|
||||
use Recurr\Rule;
|
||||
|
||||
class SchedulerWeeklyBuilder implements BuilderInterface
|
||||
{
|
||||
/**
|
||||
* @throws InvalidSchedulerException
|
||||
*/
|
||||
public function build(Rule $rule, SchedulerInterface $scheduler): Rule
|
||||
{
|
||||
try {
|
||||
$rule->setFreq('WEEKLY');
|
||||
|
||||
if ($scheduler->isScheduledWeekDays()) {
|
||||
$days = SchedulerEnum::getWeekDays();
|
||||
} else {
|
||||
$days = [$scheduler->getScheduleDay()];
|
||||
}
|
||||
|
||||
$rule->setByDay($days);
|
||||
} catch (InvalidArgument|InvalidRRule) {
|
||||
throw new InvalidSchedulerException();
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler;
|
||||
|
||||
use Recurr\Rule;
|
||||
|
||||
interface BuilderInterface
|
||||
{
|
||||
public function build(Rule $rule, SchedulerInterface $scheduler);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Command;
|
||||
|
||||
use Mautic\ReportBundle\Exception\FileIOException;
|
||||
use Mautic\ReportBundle\Model\ReportCleanup;
|
||||
use Mautic\ReportBundle\Model\ReportExporter;
|
||||
use Mautic\ReportBundle\Scheduler\Option\ExportOption;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'mautic:reports:scheduler',
|
||||
description: "Processes scheduler for report's export"
|
||||
)]
|
||||
class ExportSchedulerCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private ReportExporter $reportExporter,
|
||||
private ReportCleanup $reportCleanup,
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->addOption('--report', 'report', InputOption::VALUE_OPTIONAL, 'ID of report. Process all reports if not set.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$report = $input->getOption('report');
|
||||
|
||||
if (!is_null($report) && !is_numeric($report)) {
|
||||
$output->writeln('<error>'.$this->translator->trans('mautic.report.schedule.command.invalid_parameter').'</error>');
|
||||
|
||||
return Command::INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
$exportOption = new ExportOption((int) $report);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$output->writeln('<error>'.$this->translator->trans('mautic.report.schedule.command.invalid_parameter').'</error>');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($exportOption->getReportId()) {
|
||||
$this->reportCleanup->cleanup($exportOption->getReportId());
|
||||
} else {
|
||||
$this->reportCleanup->cleanupAll();
|
||||
}
|
||||
|
||||
$this->reportExporter->processExport($exportOption);
|
||||
|
||||
$output->writeln('<info>'.$this->translator->trans('mautic.report.schedule.command.finished').'</info>');
|
||||
} catch (FileIOException $e) {
|
||||
$output->writeln('<error>'.$e->getMessage().'</error>');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Date;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\Builder\SchedulerBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\Entity\SchedulerEntity;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\NoScheduleException;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\NotSupportedScheduleTypeException;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
|
||||
class DateBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private SchedulerBuilder $schedulerBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isScheduled
|
||||
* @param string $scheduleUnit
|
||||
* @param string $scheduleDay
|
||||
* @param string $scheduleMonthFrequency
|
||||
*/
|
||||
public function getPreviewDays($isScheduled, $scheduleUnit, $scheduleDay, $scheduleMonthFrequency): array
|
||||
{
|
||||
$entity = new SchedulerEntity($isScheduled, $scheduleUnit, $scheduleDay, $scheduleMonthFrequency);
|
||||
$count = $entity->isScheduledNow() ? 1 : 10;
|
||||
|
||||
try {
|
||||
$recurrences = $this->schedulerBuilder->getNextEvents($entity, $count);
|
||||
} catch (InvalidSchedulerException|NotSupportedScheduleTypeException) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dates = [];
|
||||
foreach ($recurrences as $recurrence) {
|
||||
$dates[] = $recurrence->getStart();
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*
|
||||
* @throws NoScheduleException
|
||||
*/
|
||||
public function getNextEvent(SchedulerInterface $scheduler)
|
||||
{
|
||||
try {
|
||||
$recurrences = $this->schedulerBuilder->getNextEvent($scheduler);
|
||||
} catch (InvalidSchedulerException|NotSupportedScheduleTypeException) {
|
||||
throw new NoScheduleException();
|
||||
}
|
||||
|
||||
if (empty($recurrences[0])) {
|
||||
throw new NoScheduleException();
|
||||
}
|
||||
|
||||
return $recurrences[0]->getStart();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Entity;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\Enum\SchedulerEnum;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
|
||||
class SchedulerEntity implements SchedulerInterface
|
||||
{
|
||||
/**
|
||||
* @param bool $isScheduled
|
||||
* @param string|null $scheduleUnit
|
||||
* @param string|null $scheduleDay
|
||||
* @param string|null $scheduleMonthFrequency
|
||||
*/
|
||||
public function __construct(
|
||||
private $isScheduled,
|
||||
private $scheduleUnit,
|
||||
private $scheduleDay,
|
||||
private $scheduleMonthFrequency,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isScheduled()
|
||||
{
|
||||
return $this->isScheduled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScheduleUnit()
|
||||
{
|
||||
return $this->scheduleUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScheduleDay()
|
||||
{
|
||||
return $this->scheduleDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScheduleMonthFrequency()
|
||||
{
|
||||
return $this->scheduleMonthFrequency;
|
||||
}
|
||||
|
||||
public function isScheduledNow(): bool
|
||||
{
|
||||
return SchedulerEnum::UNIT_NOW === $this->getScheduleUnit();
|
||||
}
|
||||
|
||||
public function isScheduledDaily(): bool
|
||||
{
|
||||
return SchedulerEnum::UNIT_DAILY === $this->getScheduleUnit();
|
||||
}
|
||||
|
||||
public function isScheduledWeekly(): bool
|
||||
{
|
||||
return SchedulerEnum::UNIT_WEEKLY === $this->getScheduleUnit();
|
||||
}
|
||||
|
||||
public function isScheduledMonthly(): bool
|
||||
{
|
||||
return SchedulerEnum::UNIT_MONTHLY === $this->getScheduleUnit();
|
||||
}
|
||||
|
||||
public function isScheduledWeekDays(): bool
|
||||
{
|
||||
return SchedulerEnum::DAY_WEEK_DAYS === $this->getScheduleDay();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Enum;
|
||||
|
||||
class SchedulerEnum
|
||||
{
|
||||
public const UNIT_NOW = 'NOW';
|
||||
|
||||
public const UNIT_DAILY = 'DAILY';
|
||||
|
||||
public const UNIT_WEEKLY = 'WEEKLY'; // Defined in report.js too
|
||||
|
||||
public const UNIT_MONTHLY = 'MONTHLY'; // Defined in report.js too
|
||||
|
||||
public const DAY_MO = 'MO';
|
||||
|
||||
public const DAY_TU = 'TU';
|
||||
|
||||
public const DAY_WE = 'WE';
|
||||
|
||||
public const DAY_TH = 'TH';
|
||||
|
||||
public const DAY_FR = 'FR';
|
||||
|
||||
public const DAY_SA = 'SA';
|
||||
|
||||
public const DAY_SU = 'SU';
|
||||
|
||||
public const DAY_WEEK_DAYS = 'WEEK_DAYS';
|
||||
|
||||
public const MONTH_FREQUENCY_FIRST = '1';
|
||||
|
||||
public const MONTH_FREQUENCY_LAST = '-1';
|
||||
|
||||
public static function getUnitEnumForSelect(): array
|
||||
{
|
||||
return [
|
||||
'mautic.report.schedule.unit.now' => self::UNIT_NOW,
|
||||
'mautic.report.schedule.unit.day' => self::UNIT_DAILY,
|
||||
'mautic.report.schedule.unit.week' => self::UNIT_WEEKLY,
|
||||
'mautic.report.schedule.unit.month' => self::UNIT_MONTHLY,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getDayEnumForSelect(): array
|
||||
{
|
||||
return [
|
||||
'mautic.report.schedule.day.monday' => self::DAY_MO,
|
||||
'mautic.report.schedule.day.tuesday' => self::DAY_TU,
|
||||
'mautic.report.schedule.day.wednesday' => self::DAY_WE,
|
||||
'mautic.report.schedule.day.thursday' => self::DAY_TH,
|
||||
'mautic.report.schedule.day.friday' => self::DAY_FR,
|
||||
'mautic.report.schedule.day.saturday' => self::DAY_SA,
|
||||
'mautic.report.schedule.day.sunday' => self::DAY_SU,
|
||||
'mautic.report.schedule.day.week_days' => self::DAY_WEEK_DAYS,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getMonthFrequencyForSelect(): array
|
||||
{
|
||||
return [
|
||||
'mautic.report.schedule.month_frequency.first' => self::MONTH_FREQUENCY_FIRST,
|
||||
'mautic.report.schedule.month_frequency.last' => self::MONTH_FREQUENCY_LAST,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getWeekDays(): array
|
||||
{
|
||||
return [
|
||||
self::DAY_MO,
|
||||
self::DAY_TU,
|
||||
self::DAY_WE,
|
||||
self::DAY_TH,
|
||||
self::DAY_FR,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\EventListener;
|
||||
|
||||
use Mautic\ReportBundle\Event\ReportEvent;
|
||||
use Mautic\ReportBundle\ReportEvents;
|
||||
use Mautic\ReportBundle\Scheduler\Model\SchedulerPlanner;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class ReportSchedulerSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private SchedulerPlanner $schedulerPlanner,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [ReportEvents::REPORT_POST_SAVE => ['onReportSave', 0]];
|
||||
}
|
||||
|
||||
public function onReportSave(ReportEvent $event): ReportEvent
|
||||
{
|
||||
$report = $event->getReport();
|
||||
|
||||
$this->schedulerPlanner->computeScheduler($report);
|
||||
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Exception;
|
||||
|
||||
class InvalidSchedulerException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Exception;
|
||||
|
||||
class NoScheduleException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Exception;
|
||||
|
||||
class NotSupportedScheduleTypeException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Exception;
|
||||
|
||||
class ScheduleNotValidException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Factory;
|
||||
|
||||
use Mautic\ReportBundle\Scheduler\Builder\SchedulerDailyBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\Builder\SchedulerMonthBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\Builder\SchedulerNowBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\Builder\SchedulerWeeklyBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\BuilderInterface;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\NotSupportedScheduleTypeException;
|
||||
use Mautic\ReportBundle\Scheduler\SchedulerInterface;
|
||||
|
||||
class SchedulerTemplateFactory
|
||||
{
|
||||
/**
|
||||
* @throws NotSupportedScheduleTypeException
|
||||
*/
|
||||
public function getBuilder(SchedulerInterface $scheduler): BuilderInterface
|
||||
{
|
||||
if ($scheduler->isScheduledNow()) {
|
||||
return new SchedulerNowBuilder();
|
||||
}
|
||||
if ($scheduler->isScheduledDaily()) {
|
||||
return new SchedulerDailyBuilder();
|
||||
}
|
||||
if ($scheduler->isScheduledWeekly()) {
|
||||
return new SchedulerWeeklyBuilder();
|
||||
}
|
||||
if ($scheduler->isScheduledMonthly()) {
|
||||
return new SchedulerMonthBuilder();
|
||||
}
|
||||
|
||||
throw new NotSupportedScheduleTypeException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Model;
|
||||
|
||||
use Mautic\CoreBundle\Exception\FileInvalidException;
|
||||
use Mautic\CoreBundle\Exception\FilePathException;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\FilePathResolver;
|
||||
use Mautic\CoreBundle\Helper\FileProperties;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Mautic\ReportBundle\Exception\FileTooBigException;
|
||||
|
||||
class FileHandler
|
||||
{
|
||||
private const REPORTS_DIR = 'csv_reports';
|
||||
|
||||
public function __construct(
|
||||
private FilePathResolver $filePathResolver,
|
||||
private FileProperties $fileProperties,
|
||||
private CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileInvalidException
|
||||
* @throws FileTooBigException
|
||||
*/
|
||||
public function fileCanBeAttached(string $filePath): void
|
||||
{
|
||||
$fileSize = $this->fileProperties->getFileSize($filePath);
|
||||
$maxFileSize = (int) $this->coreParametersHelper->get('report_export_max_filesize_in_bytes');
|
||||
|
||||
if ($fileSize > $maxFileSize) {
|
||||
throw new FileTooBigException("File {$filePath} has {$fileSize} bytes which is more than the limit of {$maxFileSize} bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zips the file and returns the path where the zip file was created.
|
||||
*
|
||||
* @throws FilePathException
|
||||
*/
|
||||
public function zipIt(string $originalFilePath): string
|
||||
{
|
||||
$zipFilePath = str_replace('.csv', '.zip', $originalFilePath);
|
||||
$zipArchive = new \ZipArchive();
|
||||
|
||||
if (true === $zipArchive->open($zipFilePath, \ZipArchive::OVERWRITE | \ZipArchive::CREATE)) {
|
||||
$zipArchive->addFile($originalFilePath, 'report.csv');
|
||||
$zipArchive->close();
|
||||
|
||||
return $zipFilePath;
|
||||
}
|
||||
|
||||
throw new FilePathException("Could not create zip archive at {$zipFilePath}. {$zipArchive->getStatusString()}");
|
||||
}
|
||||
|
||||
public function getPathToCompressedCsvFileForReport(Report $report): string
|
||||
{
|
||||
return $this->getPathToCompressedCsvFileForReportId($report->getId());
|
||||
}
|
||||
|
||||
public function getPathToCompressedCsvFileForReportId(int $reportId): string
|
||||
{
|
||||
return $this->getCompressedCsvFileForReportDir()."/report_{$reportId}.zip";
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore as it calls PHP function only.
|
||||
*/
|
||||
public function compressedCsvFileForReportExists(Report $report): bool
|
||||
{
|
||||
$filePath = $this->getPathToCompressedCsvFileForReport($report);
|
||||
|
||||
return file_exists($filePath);
|
||||
}
|
||||
|
||||
public function moveZipToPermanentLocation(Report $report, string $originalPath): void
|
||||
{
|
||||
$compressedCsvPath = $this->getPathToCompressedCsvFileForReport($report);
|
||||
|
||||
$this->filePathResolver->delete($compressedCsvPath);
|
||||
$this->filePathResolver->createDirectory(dirname($compressedCsvPath));
|
||||
$this->filePathResolver->move($originalPath, $compressedCsvPath);
|
||||
}
|
||||
|
||||
public function delete(string $filePath): void
|
||||
{
|
||||
$this->filePathResolver->delete($filePath);
|
||||
}
|
||||
|
||||
public function deleteCompressedCsvFileForReportId(int $reportId): void
|
||||
{
|
||||
$filePath = $this->getPathToCompressedCsvFileForReportId($reportId);
|
||||
if (file_exists($filePath)) {
|
||||
$this->delete($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompressedCsvFileForReportDir(): string
|
||||
{
|
||||
$reportDir = $this->coreParametersHelper->get('report_temp_dir');
|
||||
|
||||
return $reportDir.'/'.self::REPORTS_DIR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Model;
|
||||
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class MessageSchedule
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
private UrlGeneratorInterface $router,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getMessageForAttachedFile(Report $report): string
|
||||
{
|
||||
$link = $this->router->generate('mautic_report_view', ['objectId' => $report->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$date = new \DateTime();
|
||||
|
||||
return $this->translator->trans(
|
||||
'mautic.report.schedule.email.message',
|
||||
['%report_name%' => $report->getName(), '%date%' => $date->format('Y-m-d'), '%link%' => $link]
|
||||
);
|
||||
}
|
||||
|
||||
public function getMessageForLinkedFile(Report $report): string
|
||||
{
|
||||
$link = $this->router->generate('mautic_report_download', ['reportId' => $report->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
|
||||
return $this->translator->trans(
|
||||
'mautic.report.schedule.email.message_file_linked',
|
||||
['%report_name%' => $report->getName(), '%link%' => $link]
|
||||
);
|
||||
}
|
||||
|
||||
public function getSubject(Report $report): string
|
||||
{
|
||||
$date = new \DateTime();
|
||||
|
||||
return $this->translator->trans(
|
||||
'mautic.report.schedule.email.subject',
|
||||
['%report_name%' => $report->getName(), '%date%' => $date->format('Y-m-d')]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Model;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Mautic\ReportBundle\Entity\Scheduler;
|
||||
use Mautic\ReportBundle\Entity\SchedulerRepository;
|
||||
use Mautic\ReportBundle\Scheduler\Date\DateBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\NoScheduleException;
|
||||
|
||||
class SchedulerPlanner
|
||||
{
|
||||
/**
|
||||
* @var SchedulerRepository
|
||||
*/
|
||||
private \Doctrine\ORM\EntityRepository $schedulerRepository;
|
||||
|
||||
public function __construct(
|
||||
private DateBuilder $dateBuilder,
|
||||
private EntityManager $entityManager,
|
||||
) {
|
||||
$this->schedulerRepository = $entityManager->getRepository(Scheduler::class);
|
||||
}
|
||||
|
||||
public function computeScheduler(Report $report): void
|
||||
{
|
||||
$this->removeSchedulerOfReport($report);
|
||||
$this->planScheduler($report);
|
||||
}
|
||||
|
||||
private function planScheduler(Report $report): void
|
||||
{
|
||||
try {
|
||||
$date = $this->dateBuilder->getNextEvent($report);
|
||||
} catch (NoScheduleException) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scheduler = new Scheduler($report, $date);
|
||||
$this->entityManager->persist($scheduler);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
private function removeSchedulerOfReport(Report $report): void
|
||||
{
|
||||
$scheduler = $this->schedulerRepository->getSchedulerByReport($report);
|
||||
if (!$scheduler) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->entityManager->remove($scheduler);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Model;
|
||||
|
||||
use Mautic\CoreBundle\Form\DataTransformer\ArrayStringTransformer;
|
||||
use Mautic\EmailBundle\Helper\MailHelper;
|
||||
use Mautic\ReportBundle\Entity\Scheduler;
|
||||
use Mautic\ReportBundle\Event\PermanentReportFileCreatedEvent;
|
||||
use Mautic\ReportBundle\Exception\FileTooBigException;
|
||||
use Mautic\ReportBundle\ReportEvents;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class SendSchedule
|
||||
{
|
||||
private MailHelper $mailer;
|
||||
|
||||
public function __construct(
|
||||
MailHelper $mailer,
|
||||
private MessageSchedule $messageSchedule,
|
||||
private FileHandler $fileHandler,
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
) {
|
||||
$this->mailer = $mailer->getMailer();
|
||||
}
|
||||
|
||||
public function send(Scheduler $scheduler, $csvFilePath): void
|
||||
{
|
||||
$this->mailer->reset(true);
|
||||
|
||||
$transformer = new ArrayStringTransformer();
|
||||
$report = $scheduler->getReport();
|
||||
$emails = $transformer->reverseTransform($report->getToAddress());
|
||||
$subject = $this->messageSchedule->getSubject($report);
|
||||
$message = $this->messageSchedule->getMessageForAttachedFile($report);
|
||||
|
||||
try {
|
||||
// Try to send the CSV file as an email attachement.
|
||||
$this->fileHandler->fileCanBeAttached($csvFilePath);
|
||||
$this->mailer->attachFile($csvFilePath, basename($csvFilePath), 'text/csv');
|
||||
} catch (FileTooBigException) {
|
||||
$zipFilePath = $this->fileHandler->zipIt($csvFilePath);
|
||||
try {
|
||||
// Try to send the ZIP file as an email attachement.
|
||||
$this->fileHandler->fileCanBeAttached($zipFilePath);
|
||||
$this->mailer->attachFile($zipFilePath, basename($zipFilePath), 'application/zip');
|
||||
} catch (FileTooBigException) {
|
||||
// Send the ZIP file as link in the email message.
|
||||
$this->fileHandler->moveZipToPermanentLocation($report, $zipFilePath);
|
||||
$message = $this->messageSchedule->getMessageForLinkedFile($report);
|
||||
$event = new PermanentReportFileCreatedEvent($report);
|
||||
$this->eventDispatcher->dispatch($event, ReportEvents::REPORT_PERMANENT_FILE_CREATED);
|
||||
}
|
||||
}
|
||||
|
||||
$this->mailer->setTo($emails);
|
||||
$this->mailer->setSubject($subject);
|
||||
$this->mailer->setBody($message);
|
||||
$this->mailer->parsePlainText($message);
|
||||
$this->mailer->send(true);
|
||||
|
||||
// Attachment file removal will be done in \Mautic\MessengerBundle\MessageHandler\RemoveReportAttachmentHandler
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Option;
|
||||
|
||||
class ExportOption
|
||||
{
|
||||
private int $reportId;
|
||||
|
||||
/**
|
||||
* @param int|null $reportId
|
||||
*/
|
||||
public function __construct($reportId)
|
||||
{
|
||||
if (!is_null($reportId) && !is_numeric($reportId)) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
$this->reportId = (int) $reportId;
|
||||
}
|
||||
|
||||
public function getReportId(): int
|
||||
{
|
||||
return $this->reportId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler;
|
||||
|
||||
interface SchedulerInterface
|
||||
{
|
||||
public function isScheduled();
|
||||
|
||||
public function isScheduledNow(): bool;
|
||||
|
||||
public function isScheduledDaily();
|
||||
|
||||
public function isScheduledWeekly();
|
||||
|
||||
public function isScheduledMonthly();
|
||||
|
||||
public function isScheduledWeekDays();
|
||||
|
||||
public function getScheduleDay();
|
||||
|
||||
public function getScheduleMonthFrequency();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class ScheduleIsValid extends Constraint
|
||||
{
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Scheduler\Validator;
|
||||
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Mautic\ReportBundle\Scheduler\Builder\SchedulerBuilder;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\InvalidSchedulerException;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\NotSupportedScheduleTypeException;
|
||||
use Mautic\ReportBundle\Scheduler\Exception\ScheduleNotValidException;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
class ScheduleIsValidValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private SchedulerBuilder $schedulerBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Report $report
|
||||
*/
|
||||
public function validate($report, Constraint $constraint): void
|
||||
{
|
||||
if (!$report->isScheduled()) {
|
||||
$report->setAsNotScheduled();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($report->getToAddress())) {
|
||||
$this->context->buildViolation('mautic.report.schedule.to_address_required')
|
||||
->atPath('toAddress')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ($report->isScheduledDaily()) {
|
||||
$report->ensureIsDailyScheduled();
|
||||
$this->buildScheduler($report);
|
||||
|
||||
return;
|
||||
}
|
||||
if ($report->isScheduledWeekly()) {
|
||||
try {
|
||||
$report->ensureIsWeeklyScheduled();
|
||||
$this->buildScheduler($report);
|
||||
|
||||
return;
|
||||
} catch (ScheduleNotValidException) {
|
||||
$this->addReportScheduleNotValidViolation();
|
||||
}
|
||||
}
|
||||
if ($report->isScheduledMonthly()) {
|
||||
try {
|
||||
$report->ensureIsMonthlyScheduled();
|
||||
$this->buildScheduler($report);
|
||||
|
||||
return;
|
||||
} catch (ScheduleNotValidException) {
|
||||
$this->addReportScheduleNotValidViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function addReportScheduleNotValidViolation(): void
|
||||
{
|
||||
$this->context->buildViolation('mautic.report.schedule.notValid')
|
||||
->atPath('isScheduled')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
private function buildScheduler(Report $report): void
|
||||
{
|
||||
try {
|
||||
$this->schedulerBuilder->getNextEvent($report);
|
||||
|
||||
return;
|
||||
} catch (InvalidSchedulerException) {
|
||||
$message = 'mautic.report.schedule.notValid';
|
||||
} catch (NotSupportedScheduleTypeException) {
|
||||
$message = 'mautic.report.schedule.notSupportedType';
|
||||
}
|
||||
|
||||
$this->context->buildViolation($message)
|
||||
->atPath('isScheduled')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user