Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Mautic\CampaignBundle\Form\Validator\Constraints\InfiniteLoop;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventAddRemoveLeadType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('addTo', CampaignListType::class, [
|
||||
'label' => 'mautic.campaign.form.addtocampaigns',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'include_this' => $options['include_this'],
|
||||
'this_translation' => 'mautic.campaign.form.thiscampaign_restart',
|
||||
'constraints' => [new InfiniteLoop()],
|
||||
]);
|
||||
|
||||
$builder->add('removeFrom', CampaignListType::class, [
|
||||
'label' => 'mautic.campaign.form.removefromcampaigns',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'include_this' => $options['include_this'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_addremovelead';
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'include_this' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventJumpToEventType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$jumpProps = $builder->getData();
|
||||
$selected = $jumpProps['jumpToEvent'] ?? null;
|
||||
|
||||
$builder->add(
|
||||
'jumpToEvent',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [],
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.campaign.form.jump_to_event',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-onload-callback' => 'updateJumpToEventOptions',
|
||||
'data-selected' => $selected,
|
||||
],
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// Allows additional values (new events) to be selected before persisting
|
||||
$builder->get('jumpToEvent')->resetViewTransformers();
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_jump_to_event';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
final class CampaignImportType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* Build the form fields for importing campaign data.
|
||||
*
|
||||
* @param FormBuilderInterface $builder The form builder
|
||||
* @param array<string, mixed> $options The form options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'campaignFile',
|
||||
FileType::class,
|
||||
[
|
||||
'required' => true,
|
||||
'mapped' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'accept' => '.zip',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'start',
|
||||
SubmitType::class,
|
||||
[
|
||||
'label' => 'mautic.campaign.campaign.import.upload.button',
|
||||
'attr' => [
|
||||
'class' => 'btn btn-tertiary btn-sm',
|
||||
'icon' => 'ri-import-line',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignLeadSourceType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$sourceType = $options['data']['sourceType'];
|
||||
$sourceChoices = $options['source_choices'] ?? [];
|
||||
foreach ($sourceChoices as $key => $val) {
|
||||
$sourceChoices[$key] = $val.' ('.$key.')';
|
||||
}
|
||||
|
||||
switch ($sourceType) {
|
||||
case 'lists':
|
||||
$builder->add(
|
||||
'lists',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => array_flip($sourceChoices),
|
||||
'multiple' => true,
|
||||
'label' => 'mautic.campaign.leadsource.lists',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'forms':
|
||||
$builder->add(
|
||||
'forms',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => array_flip($sourceChoices),
|
||||
'multiple' => true,
|
||||
'label' => 'mautic.campaign.leadsource.forms',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$builder->add('sourceType', HiddenType::class);
|
||||
|
||||
$builder->add('droppedX', HiddenType::class);
|
||||
|
||||
$builder->add('droppedY', HiddenType::class);
|
||||
|
||||
$update = !empty($options['data'][$sourceType]);
|
||||
if (!empty($update)) {
|
||||
$btnValue = 'mautic.core.form.update';
|
||||
$btnIcon = 'ri-edit-line';
|
||||
} else {
|
||||
$btnValue = 'mautic.core.form.add';
|
||||
$btnIcon = 'ri-add-line';
|
||||
}
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class, [
|
||||
'save_text' => $btnValue,
|
||||
'save_icon' => $btnIcon,
|
||||
'save_onclick' => 'Mautic.submitCampaignSource(event)',
|
||||
'apply_text' => false,
|
||||
'container_class' => 'bottom-form-buttons',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['source_choices']);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaign_leadsource';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Mautic\CampaignBundle\Model\CampaignModel;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignListType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $canViewOther = false;
|
||||
|
||||
public function __construct(
|
||||
private CampaignModel $model,
|
||||
protected TranslatorInterface $translator,
|
||||
CorePermissions $security,
|
||||
) {
|
||||
$this->canViewOther = $security->isGranted('campaign:campaigns:viewother');
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'choices' => function (Options $options): array {
|
||||
$choices = [];
|
||||
$campaigns = $this->model->getRepository()->getPublishedCampaigns(null, null, true, $this->canViewOther);
|
||||
foreach ($campaigns as $campaign) {
|
||||
$choices[$campaign['name']] = $campaign['id'];
|
||||
}
|
||||
|
||||
// sort by language
|
||||
ksort($choices);
|
||||
|
||||
if ($options['include_this']) {
|
||||
$choices = [$options['this_translation'] => 'this'] + $choices;
|
||||
}
|
||||
|
||||
return $choices;
|
||||
},
|
||||
'placeholder' => false,
|
||||
'expanded' => false,
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'include_this' => false,
|
||||
'this_translation' => 'mautic.campaign.form.thiscampaign',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CategoryBundle\Form\Type\CategoryListType;
|
||||
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;
|
||||
use Mautic\CoreBundle\Form\EventListener\FormExitSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\PublishDownDateType;
|
||||
use Mautic\CoreBundle\Form\Type\PublishUpDateType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\ProjectBundle\Form\Type\ProjectType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<Campaign>
|
||||
*/
|
||||
class CampaignType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private CorePermissions $security,
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber(['description' => 'html']));
|
||||
$builder->addEventSubscriber(new FormExitSubscriber('campaign', $options));
|
||||
|
||||
$builder->add('name', TextType::class, [
|
||||
'label' => 'mautic.core.name',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]);
|
||||
|
||||
$builder->add('description', TextareaType::class, [
|
||||
'label' => 'mautic.core.description',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control editor'],
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$builder->add('allowRestart',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.campaign.allow_restart',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.campaign.allow_restart.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// add category
|
||||
$builder->add('category', CategoryListType::class, [
|
||||
'bundle' => 'campaign',
|
||||
]);
|
||||
|
||||
$attr = [];
|
||||
if (!empty($options['data']) && $options['data']->getId()) {
|
||||
$readonly = !$this->security->isGranted('campaign:campaigns:publish');
|
||||
$data = $options['data']->isPublished(false);
|
||||
$attr = [
|
||||
'onchange' => 'Mautic.showCampaignConfirmation(mQuery(this));',
|
||||
'data-toggle' => 'confirmation',
|
||||
'data-message' => $this->translator->trans('mautic.campaign.form.confirmation.message'),
|
||||
'data-confirm-text' => $this->translator->trans('mautic.campaign.form.confirmation.confirm_text'),
|
||||
'data-confirm-callback' => 'dismissConfirmation',
|
||||
'data-cancel-text' => $this->translator->trans('mautic.campaign.form.confirmation.cancel_text'),
|
||||
'data-cancel-callback' => 'setPublishedButtonToYes',
|
||||
'class' => 'btn btn-ghost',
|
||||
];
|
||||
} elseif (!$this->security->isGranted('campaign:campaigns:publish')) {
|
||||
$readonly = true;
|
||||
$data = false;
|
||||
} else {
|
||||
$readonly = false;
|
||||
$data = false;
|
||||
}
|
||||
|
||||
$attr['readonly'] = $readonly;
|
||||
|
||||
$builder->add('isPublished', YesNoButtonGroupType::class, [
|
||||
'data' => $data,
|
||||
'attr' => $attr,
|
||||
]);
|
||||
|
||||
$builder->add('publishUp', PublishUpDateType::class);
|
||||
$builder->add('publishDown', PublishDownDateType::class);
|
||||
|
||||
$builder->add('sessionId', HiddenType::class, [
|
||||
'mapped' => false,
|
||||
]);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
|
||||
$builder->add('projects', ProjectType::class);
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class, [
|
||||
'pre_extra_buttons' => [
|
||||
[
|
||||
'name' => 'builder',
|
||||
'label' => 'mautic.campaign.campaign.launch.builder',
|
||||
'attr' => [
|
||||
'class' => 'btn btn-tertiary btn-dnd',
|
||||
'icon' => 'ri-organization-chart',
|
||||
'onclick' => 'Mautic.launchCampaignEditor();',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$builder->add('version', HiddenType::class, [
|
||||
'mapped' => false,
|
||||
]);
|
||||
|
||||
$builder->add('campaignElements', HiddenType::class, [
|
||||
'mapped' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Campaign::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
|
||||
use Symfony\Component\Validator\Constraints\Range;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ConfigType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'campaign_time_wait_on_event_false',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.campaignconfig.campaign_time_wait_on_event_false',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'help' => 'mautic.campaignconfig.campaign_time_wait_on_event_false_help',
|
||||
'data' => $options['data']['campaign_time_wait_on_event_false'],
|
||||
'choices' => [
|
||||
'mautic.core.never' => 'null',
|
||||
'15 mn' => 'PT15M',
|
||||
'30 mn' => 'PT30M',
|
||||
'45 mn' => 'PT45M',
|
||||
'1 h' => 'PT1H',
|
||||
'2 h' => 'PT2H',
|
||||
'4 h' => 'PT4H',
|
||||
'8 h' => 'PT8H',
|
||||
'12 h' => 'PT12H',
|
||||
'24 h' => 'PT1D',
|
||||
'3 days' => 'PT3D',
|
||||
'5 days' => 'PT5D',
|
||||
'1 week' => 'PT14D',
|
||||
'3 months' => 'P3M',
|
||||
],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.campaignconfig.campaign_time_wait_on_event_false_tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'campaign_by_range',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.campaignconfig.campaign_by_range',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.campaignconfig.campaign_by_range.tooltip',
|
||||
],
|
||||
'data' => (bool) ($options['data']['campaign_by_range'] ?? false),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'campaign_use_summary',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.campaignconfig.use_summary',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.campaignconfig.use_summary.tooltip',
|
||||
],
|
||||
'data' => (bool) ($options['data']['campaign_use_summary'] ?? false),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'campaign_email_stats_enabled',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.campaignconfig.campaign_email_stats_enabled',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'data' => $options['data']['campaign_email_stats_enabled'] ?? true,
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.campaignconfig.campaign_email_stats_enabled.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'peak_interaction_timer_best_default_hour_start',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.config.peak_interaction_timer.best_default_hour_start',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.config.peak_interaction_timer.best_default_hour_start.tooltip',
|
||||
],
|
||||
'data' => $options['data']['peak_interaction_timer_best_default_hour_start'] ?? 9,
|
||||
'constraints' => [
|
||||
new Range([
|
||||
'min' => 0,
|
||||
'max' => 23,
|
||||
]),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'peak_interaction_timer_best_default_hour_end',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.config.peak_interaction_timer.best_default_hour_end',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.config.peak_interaction_timer.best_default_hour_end.tooltip',
|
||||
],
|
||||
'data' => $options['data']['peak_interaction_timer_best_default_hour_end'] ?? 12,
|
||||
'constraints' => [
|
||||
new Range([
|
||||
'min' => 0,
|
||||
'max' => 23,
|
||||
]),
|
||||
new Callback(
|
||||
function ($hourEnd, ExecutionContextInterface $context): void {
|
||||
$data = $context->getRoot()->getData();
|
||||
$hourStart = $data['campaignconfig']['peak_interaction_timer_best_default_hour_start'] ?? null;
|
||||
if (null !== $hourStart && null !== $hourEnd && $hourStart >= $hourEnd) {
|
||||
$context->buildViolation('mautic.config.peak_interaction_timer.best_default_hour.validation.range')->addViolation();
|
||||
}
|
||||
}
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'peak_interaction_timer_best_default_days',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.config.peak_interaction_timer.best_default_days',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.config.peak_interaction_timer.best_default_days.tooltip',
|
||||
],
|
||||
'choices' => [
|
||||
'mautic.core.date.monday' => 1,
|
||||
'mautic.core.date.tuesday' => 2,
|
||||
'mautic.core.date.wednesday' => 3,
|
||||
'mautic.core.date.thursday' => 4,
|
||||
'mautic.core.date.friday' => 5,
|
||||
'mautic.core.date.saturday' => 6,
|
||||
'mautic.core.date.sunday' => 7,
|
||||
],
|
||||
'data' => $options['data']['peak_interaction_timer_best_default_days'] ?? [2, 3, 4],
|
||||
'multiple' => true,
|
||||
'required' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'peak_interaction_timer_cache_timeout',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.config.peak_interaction_timer.cache_timeout',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.config.peak_interaction_timer.cache_timeout.tooltip',
|
||||
],
|
||||
'choices' => [
|
||||
'mautic.config.peak_interaction_timer.cache.off' => 0,
|
||||
'1 '.$this->translator->trans('mautic.campaign.event.intervalunit.d', ['%count%' => 1]) => 1440,
|
||||
'7 '.$this->translator->trans('mautic.campaign.event.intervalunit.d', ['%count%' => 7]) => 10080,
|
||||
'1 '.$this->translator->trans('mautic.campaign.event.intervalunit.m', ['%count%' => 1]) => 43800,
|
||||
],
|
||||
'data' => $options['data']['peak_interaction_timer_cache_timeout'] ?? 43800,
|
||||
'constraints' => [
|
||||
new GreaterThanOrEqual([
|
||||
'value' => 0,
|
||||
]),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'peak_interaction_timer_fetch_interactions_from',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.config.peak_interaction_timer.fetch_interactions_from',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.config.peak_interaction_timer.fetch_interactions_from.tooltip',
|
||||
],
|
||||
'choices' => [
|
||||
'mautic.config.peak_interaction_timer.fetch.from_30_days' => '-30 days',
|
||||
'mautic.config.peak_interaction_timer.fetch.from_60_days' => '-60 days',
|
||||
'mautic.config.peak_interaction_timer.fetch.from_90_days' => '-90 days',
|
||||
],
|
||||
'data' => $options['data']['peak_interaction_timer_fetch_interactions_from'] ?? '-60 days',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'peak_interaction_timer_fetch_limit',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.config.peak_interaction_timer.fetch_limit',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.config.peak_interaction_timer.fetch_limit.tooltip',
|
||||
],
|
||||
'data' => $options['data']['peak_interaction_timer_fetch_limit'] ?? 50,
|
||||
'constraints' => [
|
||||
new GreaterThanOrEqual([
|
||||
'value' => 10,
|
||||
]),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignconfig';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class EventCanvasSettingsType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('droppedX', HiddenType::class);
|
||||
|
||||
$builder->add('droppedY', HiddenType::class);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_canvassettings';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Type;
|
||||
|
||||
use Mautic\CampaignBundle\Executioner\Scheduler\Mode\Optimized as OptimizedScheduler;
|
||||
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\ButtonGroupType;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\PropertiesTrait;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class EventType extends AbstractType
|
||||
{
|
||||
use PropertiesTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$masks = [];
|
||||
|
||||
$builder->add(
|
||||
'name',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.name',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'anchor',
|
||||
HiddenType::class,
|
||||
[
|
||||
'label' => false,
|
||||
]
|
||||
);
|
||||
|
||||
if (in_array($options['data']['eventType'], ['action', 'condition'])) {
|
||||
$label = 'mautic.campaign.form.type';
|
||||
|
||||
$choices = [
|
||||
'immediate' => 'mautic.campaign.form.type.immediate',
|
||||
'interval' => 'mautic.campaign.form.type.interval',
|
||||
'date' => 'mautic.campaign.form.type.date',
|
||||
];
|
||||
|
||||
if (in_array($options['data']['type'], OptimizedScheduler::AVAILABLE_FOR_EVENTS)) {
|
||||
$choices['optimized'] = 'mautic.campaign.form.type.optimized';
|
||||
}
|
||||
|
||||
if (isset($options['data']['anchor']) && isset($options['data']['anchorEventType'])
|
||||
&& 'no' === $options['data']['anchor']
|
||||
&& 'condition' !== $options['data']['anchorEventType']
|
||||
&& 'condition' !== $options['data']['eventType']) {
|
||||
$label .= '_inaction';
|
||||
|
||||
unset($choices['immediate']);
|
||||
$choices['interval'] = $choices['interval'].'_inaction';
|
||||
$choices['date'] = $choices['date'].'_inaction';
|
||||
}
|
||||
$default = array_key_first($choices);
|
||||
|
||||
$triggerMode = (empty($options['data']['triggerMode'])) ? $default : $options['data']['triggerMode'];
|
||||
$builder->add(
|
||||
'triggerMode',
|
||||
ButtonGroupType::class,
|
||||
[
|
||||
'choices' => array_flip($choices),
|
||||
'expanded' => true,
|
||||
'multiple' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'label' => $label,
|
||||
'placeholder' => false,
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'onchange' => 'Mautic.campaignToggleTimeframes();',
|
||||
'tooltip' => 'mautic.campaign.form.type.help',
|
||||
],
|
||||
'data' => $triggerMode,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'triggerDate',
|
||||
DateTimeType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-calendar-line',
|
||||
'data-toggle' => 'datetime',
|
||||
],
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => 'yyyy-MM-dd HH:mm',
|
||||
'data' => $this->getTimeValue($options['data'], 'triggerDate'),
|
||||
]
|
||||
);
|
||||
|
||||
$data = (!isset($options['data']['triggerInterval']) || '' === $options['data']['triggerInterval']
|
||||
|| null === $options['data']['triggerInterval']) ? 1 : (int) $options['data']['triggerInterval'];
|
||||
$builder->add(
|
||||
'triggerInterval',
|
||||
IntegerType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'symbol-hashtag',
|
||||
],
|
||||
'data' => $data,
|
||||
]
|
||||
);
|
||||
|
||||
$data = (!empty($options['data']['triggerIntervalUnit'])) ? $options['data']['triggerIntervalUnit'] : 'd';
|
||||
$builder->add(
|
||||
'triggerIntervalUnit',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.campaign.event.intervalunit.choice.i' => 'i',
|
||||
'mautic.campaign.event.intervalunit.choice.h' => 'h',
|
||||
'mautic.campaign.event.intervalunit.choice.d' => 'd',
|
||||
'mautic.campaign.event.intervalunit.choice.m' => 'm',
|
||||
'mautic.campaign.event.intervalunit.choice.y' => 'y',
|
||||
],
|
||||
'multiple' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'placeholder' => false,
|
||||
'required' => false,
|
||||
'data' => $data,
|
||||
]
|
||||
);
|
||||
|
||||
// I could not get Doctrine TimeType does not play well with Symfony TimeType so hacking this workaround
|
||||
$data = $this->getTimeValue($options['data'], 'triggerHour');
|
||||
$builder->add(
|
||||
'triggerHour',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => 'time',
|
||||
'data-format' => 'H:i',
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
'data' => ($data) ? $data->format('H:i') : $data,
|
||||
]
|
||||
);
|
||||
|
||||
$data = $this->getTimeValue($options['data'], 'triggerRestrictedStartHour');
|
||||
$builder->add(
|
||||
'triggerRestrictedStartHour',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => 'time',
|
||||
'data-format' => 'H:i',
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
'data' => ($data) ? $data->format('H:i') : $data,
|
||||
]
|
||||
);
|
||||
|
||||
$data = $this->getTimeValue($options['data'], 'triggerRestrictedStopHour');
|
||||
$builder->add(
|
||||
'triggerRestrictedStopHour',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => 'time',
|
||||
'data-format' => 'H:i',
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
'data' => ($data) ? $data->format('H:i') : $data,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'triggerRestrictedDaysOfWeek',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => true,
|
||||
'attr' => [
|
||||
'data-toggle' => 'time',
|
||||
'data-format' => 'H:i',
|
||||
],
|
||||
'choices' => [
|
||||
'mautic.report.schedule.day.monday' => 1,
|
||||
'mautic.report.schedule.day.tuesday' => 2,
|
||||
'mautic.report.schedule.day.wednesday' => 3,
|
||||
'mautic.report.schedule.day.thursday' => 4,
|
||||
'mautic.report.schedule.day.friday' => 5,
|
||||
'mautic.report.schedule.day.saturday' => 6,
|
||||
'mautic.report.schedule.day.sunday' => 0,
|
||||
'mautic.report.schedule.day.week_days' => -1,
|
||||
],
|
||||
'expanded' => true,
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'triggerWindow',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'choices' => [
|
||||
'mautic.campaign.form.type.trigger_window_day' => OptimizedScheduler::OPTIMIZED_TIME,
|
||||
'mautic.campaign.form.type.trigger_window_week' => OptimizedScheduler::OPTIMIZED_DAY_AND_TIME,
|
||||
],
|
||||
'data' => $options['data']['triggerWindow'] ?? 0,
|
||||
'required' => false,
|
||||
'expanded' => true,
|
||||
'placeholder' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void {
|
||||
$data = $event->getData();
|
||||
$triggerMode = $data['triggerMode'] ?? 'immediate';
|
||||
|
||||
// Do not set any trigger window when optimized mode is not used
|
||||
if ('optimized' !== $triggerMode) {
|
||||
$data['triggerWindow'] = null;
|
||||
$event->setData($data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!empty($options['settings']['formType'])) {
|
||||
$this->addPropertiesType($builder, $options, $masks);
|
||||
}
|
||||
|
||||
$builder->add('type', HiddenType::class);
|
||||
$builder->add('eventType', HiddenType::class);
|
||||
$builder->add(
|
||||
'anchorEventType',
|
||||
HiddenType::class,
|
||||
[
|
||||
'mapped' => false,
|
||||
'data' => $options['data']['anchorEventType'] ?? '',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'canvasSettings',
|
||||
EventCanvasSettingsType::class,
|
||||
[
|
||||
'label' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$update = !empty($options['data']['properties']);
|
||||
if (!empty($update)) {
|
||||
$btnValue = 'mautic.core.form.update';
|
||||
$btnIcon = 'ri-edit-line';
|
||||
} else {
|
||||
$btnValue = 'mautic.core.form.add';
|
||||
$btnIcon = 'ri-add-line';
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'save_text' => $btnValue,
|
||||
'save_icon' => $btnIcon,
|
||||
'save_onclick' => 'Mautic.submitCampaignEvent(event)',
|
||||
'cancel_onclick' => 'Mautic.cancelCampaignEvent(event)',
|
||||
'apply_text' => false,
|
||||
'container_class' => 'bottom-form-buttons',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'campaignId',
|
||||
HiddenType::class,
|
||||
[
|
||||
'mapped' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber($masks));
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['settings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime|mixed|null
|
||||
*/
|
||||
private function getTimeValue(array $data, $name)
|
||||
{
|
||||
if (empty($data[$name])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($data[$name] instanceof \DateTime) {
|
||||
return $data[$name];
|
||||
}
|
||||
|
||||
if (is_array($data[$name]) && array_key_exists('date', $data[$name])) {
|
||||
return new \DateTime($data[$name]['date']);
|
||||
} elseif (is_string($data[$name])) {
|
||||
return new \DateTime($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
final class InfiniteLoop extends Constraint
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class InfiniteLoopValidator extends ConstraintValidator
|
||||
{
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof InfiniteLoop) {
|
||||
throw new UnexpectedTypeException($constraint, InfiniteLoop::class);
|
||||
}
|
||||
|
||||
$data = $this->context->getRoot()->getData();
|
||||
|
||||
$this->validateEvent($this->context, $data['triggerMode'] ?? '', $value, (int) ($data['triggerInterval'] ?? 0), $data['triggerIntervalUnit'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $addTo
|
||||
*/
|
||||
public function validateEvent(ExecutionContextInterface $context, string $triggerMode, array $addTo, int $triggerInterval, string $triggerIntervalUnit): void
|
||||
{
|
||||
if (!in_array('this', $addTo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('immediate' === $triggerMode) {
|
||||
$context->buildViolation('mautic.campaign.infiniteloop.immediate')->addViolation();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ('interval' === $triggerMode && 'i' === $triggerIntervalUnit && $triggerInterval < 30) {
|
||||
$context->buildViolation('mautic.campaign.infiniteloop.interval')
|
||||
->setParameter('%count%', (string) $triggerInterval)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user