Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\DataTransformer;
|
||||
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\LeadBundle\Entity\LeadListRepository;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @implements DataTransformerInterface<mixed, array<mixed>|mixed>
|
||||
*/
|
||||
class FieldFilterTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $relativeDateStrings;
|
||||
|
||||
public function __construct(
|
||||
TranslatorInterface $translator,
|
||||
private array $default = [],
|
||||
) {
|
||||
$this->relativeDateStrings = LeadListRepository::getRelativeDateTranslationKeys();
|
||||
foreach ($this->relativeDateStrings as &$string) {
|
||||
$string = $translator->trans($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From DB format to form format.
|
||||
*/
|
||||
public function transform(mixed $rawFilters): mixed
|
||||
{
|
||||
if (!is_array($rawFilters)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($rawFilters as $key => $filter) {
|
||||
if (!empty($this->default)) {
|
||||
$rawFilters[$key] = array_merge($this->default, $rawFilters[$key]);
|
||||
}
|
||||
if ('datetime' === $filter['type']) {
|
||||
$bcFilter = $filter['filter'] ?? '';
|
||||
$filter = $filter['properties']['filter'] ?? $bcFilter;
|
||||
if (empty($filter) || in_array($filter, $this->relativeDateStrings) || stristr($filter[0], '-') || stristr($filter[0], '+')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dt = new DateTimeHelper($filter, 'Y-m-d H:i');
|
||||
|
||||
$rawFilters[$key]['properties']['filter'] = $dt->toLocalString();
|
||||
}
|
||||
}
|
||||
|
||||
return $rawFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form format to database format.
|
||||
*/
|
||||
public function reverseTransform(mixed $rawFilters): mixed
|
||||
{
|
||||
if (!is_array($rawFilters)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rawFilters = array_values($rawFilters);
|
||||
|
||||
foreach ($rawFilters as $k => $f) {
|
||||
if ('datetime' === $f['type']) {
|
||||
$bcFilter = $f['filter'] ?? '';
|
||||
$filter = $f['properties']['filter'] ?? $bcFilter;
|
||||
if (empty($filter) || in_array($filter, $this->relativeDateStrings) || stristr($filter[0], '-') || stristr($filter[0], '+')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dt = new DateTimeHelper($filter, 'Y-m-d H:i', 'local');
|
||||
|
||||
$rawFilters[$k]['properties']['filter'] = $dt->toUtcString();
|
||||
}
|
||||
}
|
||||
|
||||
return $rawFilters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\DataTransformer;
|
||||
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Entity\LeadFieldRepository;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
|
||||
/**
|
||||
* @implements DataTransformerInterface<LeadField|null, int|null>
|
||||
*/
|
||||
class FieldToOrderTransformer implements DataTransformerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private LeadFieldRepository $leadFieldRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an object to an integer (order).
|
||||
*
|
||||
* @param int|null $order
|
||||
*
|
||||
* @return LeadField|null
|
||||
*/
|
||||
public function transform(mixed $order): mixed
|
||||
{
|
||||
if (!$order) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->leadFieldRepository->findOneBy(['order' => $order]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a integer to an object.
|
||||
*
|
||||
* @param LeadField|null $field
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function reverseTransform(mixed $field): mixed
|
||||
{
|
||||
if (null === $field) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $field->getOrder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\DataTransformer;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* @implements DataTransformerInterface<array<mixed>|int|null, array<mixed>|object|null>
|
||||
*/
|
||||
class TagEntityModelTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @param string $repository
|
||||
* @param bool $isArray
|
||||
*/
|
||||
public function __construct(
|
||||
private EntityManager $em,
|
||||
private $repository = '',
|
||||
private $isArray = false,
|
||||
) {
|
||||
}
|
||||
|
||||
public function reverseTransform(mixed $entity): mixed
|
||||
{
|
||||
if (!$this->isArray) {
|
||||
if (is_null($entity) || !is_object($entity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $entity->getTag();
|
||||
}
|
||||
|
||||
if (is_null($entity) && !is_array($entity) && !$entity instanceof PersistentCollection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$return = [];
|
||||
foreach ($entity as $e) {
|
||||
$return[] = $e->getTag();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TransformationFailedException if object is not found
|
||||
*/
|
||||
public function transform(mixed $id): mixed
|
||||
{
|
||||
if (!$this->isArray) {
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$column = (is_numeric($id)) ? 'id' : 'tag';
|
||||
$entity = $this->em
|
||||
->getRepository($this->repository)
|
||||
->findOneBy([$column => $id]);
|
||||
|
||||
if (null === $entity) {
|
||||
throw new TransformationFailedException(sprintf('Tag with "%s" does not exist!', $id));
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
if (empty($id) || !is_array($id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$column = (is_numeric($id[0])) ? 'id' : 'tag';
|
||||
|
||||
$repo = $this->em->getRepository($this->repository);
|
||||
$prefix = $repo->getTableAlias();
|
||||
|
||||
$entities = $repo->getEntities([
|
||||
'filter' => [
|
||||
'force' => [
|
||||
[
|
||||
'column' => $prefix.'.'.$column,
|
||||
'expr' => 'in',
|
||||
'value' => $id,
|
||||
],
|
||||
],
|
||||
],
|
||||
'ignore_paginator' => true,
|
||||
]);
|
||||
|
||||
if (!count($entities)) {
|
||||
throw new TransformationFailedException(sprintf('Tags for "%s" does not exist!', implode(', ', $id)));
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the repository to use.
|
||||
*
|
||||
* @param string $repo
|
||||
*/
|
||||
public function setRepository($repo): void
|
||||
{
|
||||
$this->repository = $repo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\BooleanType;
|
||||
use Mautic\CoreBundle\Form\Type\CountryType;
|
||||
use Mautic\CoreBundle\Form\Type\LocaleType;
|
||||
use Mautic\CoreBundle\Form\Type\LookupType;
|
||||
use Mautic\CoreBundle\Form\Type\MultiselectType;
|
||||
use Mautic\CoreBundle\Form\Type\RegionType;
|
||||
use Mautic\CoreBundle\Form\Type\SelectType;
|
||||
use Mautic\CoreBundle\Form\Type\TelType;
|
||||
use Mautic\CoreBundle\Form\Type\TimezoneType;
|
||||
use Mautic\LeadBundle\Exception\FieldNotFoundException;
|
||||
use Mautic\LeadBundle\Form\Type\HtmlType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
||||
|
||||
/**
|
||||
* Provides map between Mautic 2 (Symfony 2.8) form aliases and Mautic 3 (Symfony 3.4) FQCN.
|
||||
*/
|
||||
final class FieldAliasToFqcnMap
|
||||
{
|
||||
/**
|
||||
* @format [field alias => field FQCN]
|
||||
*/
|
||||
public const MAP = [
|
||||
'boolean' => BooleanType::class,
|
||||
'country' => CountryType::class,
|
||||
'date' => DateType::class,
|
||||
'datetime' => DateTimeType::class,
|
||||
'email' => EmailType::class,
|
||||
'hidden' => HiddenType::class,
|
||||
'locale' => LocaleType::class,
|
||||
'lookup' => LookupType::class,
|
||||
'multiselect' => MultiselectType::class,
|
||||
'number' => NumberType::class,
|
||||
'region' => RegionType::class,
|
||||
'select' => SelectType::class,
|
||||
'tel' => TelType::class,
|
||||
'text' => TextType::class,
|
||||
'textarea' => TextareaType::class,
|
||||
'time' => TimeType::class,
|
||||
'timezone' => TimezoneType::class,
|
||||
'url' => UrlType::class,
|
||||
'html' => HtmlType::class,
|
||||
];
|
||||
|
||||
public static function getFqcn(string $alias): string
|
||||
{
|
||||
if (array_key_exists($alias, self::MAP)) {
|
||||
return self::MAP[$alias];
|
||||
}
|
||||
|
||||
throw new FieldNotFoundException("Field with alias {$alias} not found");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ActionAddUtmTagsType extends AbstractType
|
||||
{
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_action_addutmtags';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ActionRemoveDoNotContact extends AbstractType
|
||||
{
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_action_removedonotcontact';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class AddToCompanyActionType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
protected RouterInterface $router,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'company',
|
||||
CompanyListType::class,
|
||||
[
|
||||
'multiple' => false,
|
||||
'required' => true,
|
||||
'modal_route' => false,
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.company.choosecompany.notblank']
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$windowUrl = $this->router->generate(
|
||||
'mautic_company_action',
|
||||
[
|
||||
'objectAction' => 'new',
|
||||
'contentOnly' => 1,
|
||||
'updateSelect' => 'campaignevent_properties_company',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'newCompanyButton',
|
||||
ButtonType::class,
|
||||
[
|
||||
'attr' => [
|
||||
'class' => 'btn btn-primary btn-nospin',
|
||||
'onclick' => 'Mautic.loadNewWindow({"windowUrl": "'.$windowUrl.'"})',
|
||||
'icon' => 'ri-add-line',
|
||||
],
|
||||
'label' => 'mautic.company.new.company',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'addtocompany_action';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\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;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class BatchType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'add',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.add_to',
|
||||
'multiple' => true,
|
||||
'choices' => $options['items'],
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'remove',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.remove_from',
|
||||
'multiple' => true,
|
||||
'choices' => $options['items'],
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('ids', HiddenType::class);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(
|
||||
[
|
||||
'items',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_batch';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignActionAddDNCType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'channels',
|
||||
PreferenceChannelsType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.contact.channels',
|
||||
'multiple' => true,
|
||||
'required' => true,
|
||||
'constraints' => [
|
||||
new NotBlank(),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'reason',
|
||||
TextareaType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.dnc_reason',
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignActionRemoveDNCType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'channels',
|
||||
PreferenceChannelsType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.contact.channels',
|
||||
'multiple' => true,
|
||||
'required' => true,
|
||||
'constraints' => [
|
||||
new NotBlank(),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\DataTransformer\SecondsConversionTransformer;
|
||||
use Mautic\PageBundle\Form\Type\PageListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
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\Form\FormInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignConditionLeadPageHitType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('page_url', TextType::class, [
|
||||
'label' => 'mautic.page.point.action.form.page.url',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.page.point.action.form.page.url.descr',
|
||||
'placeholder' => 'https://',
|
||||
],
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$builder->add('page', PageListType::class, [
|
||||
'label' => 'mautic.page.campaign.condition.form.page',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.page.campaign.condition.form.page.descr',
|
||||
],
|
||||
'multiple' => false,
|
||||
'required' => false,
|
||||
'placeholder' => 'Choose Page',
|
||||
]);
|
||||
|
||||
$builder->add(
|
||||
'startDate',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.page.campaign.condition.form.startdate',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-calendar-line',
|
||||
'data-toggle' => 'datetime',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'endDate',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.page.campaign.condition.form.enddate',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-calendar-line',
|
||||
'data-toggle' => 'datetime',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$formModifier = function (FormInterface $form, $data) use ($builder): void {
|
||||
$unit = 's';
|
||||
$form->add('accumulative_time_unit', HiddenType::class, [
|
||||
'data' => $unit,
|
||||
]);
|
||||
|
||||
$secondsTransformer = new SecondsConversionTransformer($unit);
|
||||
$form->add(
|
||||
$builder->create('accumulative_time', TextType::class, [
|
||||
'label' => 'mautic.page.campaign.condition.form.timespent',
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'auto_initialize' => false,
|
||||
])
|
||||
->addViewTransformer($secondsTransformer)
|
||||
->getForm()
|
||||
);
|
||||
|
||||
$unit = 's';
|
||||
$secondsTransformer = new SecondsConversionTransformer($unit);
|
||||
$form->add('returns_within_unit', HiddenType::class, [
|
||||
'data' => $unit,
|
||||
]);
|
||||
};
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) use ($formModifier): void {
|
||||
$data = $event->getData();
|
||||
$formModifier($event->getForm(), $data);
|
||||
}
|
||||
);
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($formModifier): void {
|
||||
$data = $event->getData();
|
||||
$formModifier($event->getForm(), $data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaigncondition_lead_pageHit';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CampaignBundle\Form\Type\CampaignListType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadCampaignsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
protected ListModel $listModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('campaigns',
|
||||
CampaignListType::class, [
|
||||
'label' => 'mautic.lead.lead.events.campaigns.membership',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$builder->add(
|
||||
'dataAddedLimit',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.events.campaigns.date.added.filter',
|
||||
'data' => $options['data']['dataAddedLimit'] ?? false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'expr',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.events.campaigns.expression',
|
||||
'multiple' => false,
|
||||
'choices' => $this->listModel->getOperatorsForFieldType([
|
||||
'include' => [
|
||||
'gt',
|
||||
'lt',
|
||||
],
|
||||
]),
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-show-on' => '{"campaignevent_properties_dataAddedLimit_1":"checked"}',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'dateAdded',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.events.campaigns.date',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => 'datetime',
|
||||
'data-show-on' => '{"campaignevent_properties_dataAddedLimit_1":"checked"}',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_campaigns';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Entity\DoNotContact;
|
||||
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 CampaignEventLeadDNCType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'channels',
|
||||
PreferenceChannelsType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.contact.channels',
|
||||
'multiple' => true,
|
||||
'required' => true,
|
||||
'constraints' => [
|
||||
new NotBlank(),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'reason',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.lead.do.not.contact_bounced' => DoNotContact::BOUNCED,
|
||||
'mautic.lead.do.not.contact_unsubscribed' => DoNotContact::UNSUBSCRIBED,
|
||||
'mautic.lead.do.not.contact_manual' => DoNotContact::MANUAL,
|
||||
],
|
||||
'label' => 'mautic.lead.batch.dnc_reason',
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use DeviceDetector\Parser\Device\AbstractDeviceParser as DeviceParser;
|
||||
use DeviceDetector\Parser\OperatingSystem;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadDeviceType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'device_type',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.campaign.event.device_type',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'choices' => array_combine(DeviceParser::getAvailableDeviceTypeNames(), DeviceParser::getAvailableDeviceTypeNames()),
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'device_brand',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.campaign.event.device_brand',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'choices' => array_flip(DeviceParser::$deviceBrands),
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'device_os',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.campaign.event.device_os',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'choices' => array_combine(array_keys(OperatingSystem::getAvailableOperatingSystemFamilies()), array_keys(OperatingSystem::getAvailableOperatingSystemFamilies())),
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_device';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Helper\ArrayHelper;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\LeadBundle\Helper\FormFieldHelper;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Mautic\LeadBundle\Segment\OperatorOptions;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
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\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadFieldValueType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
protected Translator $translator,
|
||||
protected LeadModel $leadModel,
|
||||
protected FieldModel $fieldModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'field',
|
||||
LeadFieldsType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.campaign.event.field',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => false,
|
||||
'with_company_fields' => true,
|
||||
'with_tags' => true,
|
||||
'with_utm' => true,
|
||||
'placeholder' => 'mautic.core.select',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.campaign.event.field_descr',
|
||||
'onchange' => 'Mautic.updateLeadFieldValues(this)',
|
||||
],
|
||||
'required' => true,
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.core.value.required']
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// function to add 'template' choice field dynamically
|
||||
$func = function (FormEvent $e): void {
|
||||
$data = $e->getData();
|
||||
$form = $e->getForm();
|
||||
|
||||
$fieldValues = null;
|
||||
$fieldType = null;
|
||||
$choiceAttr = [];
|
||||
$operator = '=';
|
||||
|
||||
if (isset($data['field'])) {
|
||||
$field = $this->fieldModel->getRepository()->findOneBy(['alias' => $data['field']]);
|
||||
$operator = $data['operator'];
|
||||
|
||||
if ($field) {
|
||||
$properties = $field->getProperties();
|
||||
$fieldType = $field->getType();
|
||||
if (!empty($properties['list'])) {
|
||||
// Lookup/Select options
|
||||
$fieldValues = FormFieldHelper::parseList($properties['list']);
|
||||
} elseif (!empty($properties) && 'boolean' == $fieldType) {
|
||||
// Boolean options
|
||||
$fieldValues = [
|
||||
0 => $properties['no'],
|
||||
1 => $properties['yes'],
|
||||
];
|
||||
} else {
|
||||
switch ($fieldType) {
|
||||
case 'country':
|
||||
$fieldValues = FormFieldHelper::getCountryChoices();
|
||||
break;
|
||||
case 'region':
|
||||
$fieldValues = ArrayHelper::flatten(FormFieldHelper::getRegionChoices());
|
||||
break;
|
||||
case 'timezone':
|
||||
$fieldValues = ArrayHelper::flatten(FormFieldHelper::getTimezonesChoices());
|
||||
break;
|
||||
case 'locale':
|
||||
// Locales are flipped. And yes, we will flip the array again below.
|
||||
$fieldValues = array_flip(FormFieldHelper::getLocaleChoices());
|
||||
break;
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
if ('date' === $operator) {
|
||||
$fieldHelper = new FormFieldHelper();
|
||||
$fieldHelper->setTranslator($this->translator);
|
||||
$fieldValues = $fieldHelper->getDateChoices();
|
||||
$customText = $this->translator->trans('mautic.campaign.event.timed.choice.custom');
|
||||
$customValue = (empty($data['value']) || isset($fieldValues[$data['value']])) ? 'custom' : $data['value'];
|
||||
$fieldValues = array_merge(
|
||||
[
|
||||
$customValue => $customText,
|
||||
],
|
||||
$fieldValues
|
||||
);
|
||||
|
||||
$choiceAttr = function ($value, $key, $index) use ($customValue): array {
|
||||
if ($customValue === $value) {
|
||||
return ['data-custom' => 1];
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'lookup':
|
||||
case 'select':
|
||||
case 'radio':
|
||||
if (!empty($properties)) {
|
||||
$fieldValues = $properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$supportsValue = !in_array($operator, ['empty', '!empty']);
|
||||
$supportsChoices = !in_array($operator, ['empty', '!empty', 'regexp', '!regexp']);
|
||||
|
||||
// Display selectbox for a field with choices, textbox for others
|
||||
if (!empty($fieldValues) && $supportsChoices) {
|
||||
$isMultiple = in_array($operator, [OperatorOptions::INCLUDING_ANY, OperatorOptions::EXCLUDING_ANY]);
|
||||
$value = $isMultiple && !is_array($data['value']) ? [$data['value']] : $data['value'];
|
||||
$innerBuilder = $form->getConfig()->getFormFactory()->createNamedBuilder('value', ChoiceType::class, null, [
|
||||
'choices' => array_flip($fieldValues),
|
||||
'label' => 'mautic.form.field.form.value',
|
||||
'multiple' => $isMultiple,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'onchange' => 'Mautic.updateLeadFieldValueOptions(this)',
|
||||
'data-toggle' => $fieldType,
|
||||
'data-onload-callback' => 'updateLeadFieldValueOptions',
|
||||
],
|
||||
'choice_attr' => $choiceAttr,
|
||||
'required' => true,
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.core.value.required']
|
||||
),
|
||||
],
|
||||
'auto_initialize' => false,
|
||||
'data' => $value,
|
||||
]);
|
||||
|
||||
$transform = function ($value) use ($isMultiple) {
|
||||
if ($isMultiple) {
|
||||
return !is_array($value) ? (array) $value : $value;
|
||||
}
|
||||
|
||||
return is_array($value) ? reset($value) : $value;
|
||||
};
|
||||
$innerBuilder->addModelTransformer(new CallbackTransformer($transform, $transform));
|
||||
|
||||
$form->add($innerBuilder->getForm());
|
||||
} else {
|
||||
$attr = [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => $fieldType,
|
||||
'data-onload-callback' => 'updateLeadFieldValueOptions',
|
||||
];
|
||||
|
||||
if (!$supportsValue) {
|
||||
$attr['disabled'] = 'disabled';
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'value',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.form.field.form.value',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => $attr,
|
||||
'constraints' => ($supportsValue) ? [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.core.value.required']
|
||||
),
|
||||
] : [],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $this->leadModel->getOperatorsForFieldType(null == $fieldType ? 'default' : $fieldType, ['date']),
|
||||
'label' => 'mautic.lead.lead.submitaction.operator',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'onchange' => 'Mautic.updateLeadFieldValues(this)',
|
||||
],
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
// Register the function above as EventListener on PreSet and PreBind
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, $func);
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, $func);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_field_value';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\UserBundle\Form\Type\UserListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadOwnerType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'owner',
|
||||
UserListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.field.owner',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'multiple' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_owner';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadSegmentsType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'segments',
|
||||
LeadListType::class,
|
||||
[
|
||||
'global_only' => true,
|
||||
'label' => 'mautic.lead.lead.lists',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_segments';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\StageBundle\Form\Type\StageListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadStagesType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'stages',
|
||||
StageListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.field.stage',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_stages';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CampaignEventLeadTagsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'tags',
|
||||
TagType::class,
|
||||
[
|
||||
'add_transformer' => true,
|
||||
'by_reference' => false,
|
||||
'attr' => [
|
||||
'data-placeholder' => $this->translator->trans('mautic.lead.tags.select_or_create'),
|
||||
'data-no-results-text' => $this->translator->trans('mautic.lead.tags.enter_to_create'),
|
||||
'data-allow-add' => 'true',
|
||||
'onchange' => 'Mautic.createLeadTag(this)',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'campaignevent_lead_tags';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Provider\TypeOperatorProviderInterface;
|
||||
use Mautic\LeadBundle\Segment\OperatorOptions;
|
||||
use Mautic\PointBundle\Form\Type\GroupListType;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<CampaignEventPointType>
|
||||
*/
|
||||
class CampaignEventPointType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private TypeOperatorProviderInterface $typeOperatorProvider,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.campaign.event.point_operator',
|
||||
'multiple' => false,
|
||||
'choices' => $this->typeOperatorProvider->getOperatorsIncluding([
|
||||
OperatorOptions::EQUAL_TO,
|
||||
OperatorOptions::NOT_EQUAL_TO,
|
||||
OperatorOptions::GREATER_THAN,
|
||||
OperatorOptions::LESS_THAN,
|
||||
OperatorOptions::GREATER_THAN_OR_EQUAL,
|
||||
OperatorOptions::LESS_THAN_OR_EQUAL,
|
||||
]),
|
||||
'required' => true,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'score',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.campaign.event.point_score',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'scale' => 0,
|
||||
'required' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('group', GroupListType::class, [
|
||||
'label' => 'mautic.lead.campaign.event.point_group',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.campaign.event.point_group.help',
|
||||
],
|
||||
'required' => false,
|
||||
'by_reference' => false,
|
||||
'return_entity' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ChangeOwnerType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private UserModel $userModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'owner',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.add_to',
|
||||
'multiple' => false,
|
||||
'choices' => $this->userModel->getOwnerListChoices(),
|
||||
'required' => true,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotEqualTo;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CompanyChangeScoreActionType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'score',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.events.changecompanyscore',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'scale' => 0,
|
||||
'data' => $options['data']['score'] ?? 0,
|
||||
'constraints' => [
|
||||
new NotEqualTo(
|
||||
[
|
||||
'value' => 0,
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'scorecontactscompanies_action';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\EntityLookupType;
|
||||
use Mautic\LeadBundle\Entity\CompanyRepository;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CompanyListType extends AbstractType
|
||||
{
|
||||
public const DEFAULT_LIMIT = 100;
|
||||
|
||||
public function __construct(
|
||||
private CompanyRepository $companyRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'label' => 'mautic.lead.lead.companies',
|
||||
'entity_label_column' => 'companyname',
|
||||
'modal_route' => 'mautic_company_action',
|
||||
'modal_header' => 'mautic.company.new.company',
|
||||
'model' => 'lead.company',
|
||||
'ajax_lookup_action' => 'lead:getLookupChoiceList',
|
||||
'model_lookup_method' => 'getLookupResults',
|
||||
'lookup_arguments' => fn (Options $options): array => [
|
||||
'type' => 'lead.company',
|
||||
'limit' => self::DEFAULT_LIMIT,
|
||||
] + ((isset($options['model_lookup_method']) && ('getSimpleLookupResults' === $options['model_lookup_method'])) ? ['exclude' => $options['main_entity']] : []),
|
||||
'multiple' => true,
|
||||
'main_entity' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
$data = $form->getData();
|
||||
if ($data) {
|
||||
$selectedIds = is_array($data) ? $data : [$data];
|
||||
$existingChoices = array_column($view->vars['choices'], 'value');
|
||||
$missingIds = array_diff($selectedIds, $existingChoices);
|
||||
|
||||
if ($missingIds) {
|
||||
$missingCompanies = $this->companyRepository->findBy(['id' => $missingIds]);
|
||||
foreach ($missingCompanies as $company) {
|
||||
$view->vars['choices'][] = new ChoiceView(
|
||||
$company->getId(),
|
||||
(string) $company->getId(),
|
||||
$company->getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return EntityLookupType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CompanyMergeType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'company_to_merge',
|
||||
CompanyListType::class,
|
||||
[
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.company.to.merge.into',
|
||||
'required' => true,
|
||||
'modal_route' => false,
|
||||
'main_entity' => $options['main_entity'],
|
||||
'model_lookup_method' => $options['model_lookup_method'],
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.company.choosecompany.notblank']
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.lead.merge',
|
||||
'save_icon' => 'ri-building-2-line',
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefined(
|
||||
['main_entity', 'model_lookup_method']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Form\DataTransformer\IdToEntityModelTransformer;
|
||||
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\ProjectBundle\Form\Type\ProjectType;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Form\Type\UserListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<Company>
|
||||
*/
|
||||
class CompanyType extends AbstractType
|
||||
{
|
||||
use EntityFieldsBuildFormTrait;
|
||||
|
||||
public function __construct(
|
||||
private EntityManager $em,
|
||||
protected RouterInterface $router,
|
||||
protected TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$cleaningRules = $this->getFormFields($builder, $options, 'company');
|
||||
$cleaningRules['companyemail'] = 'email';
|
||||
|
||||
$transformer = new IdToEntityModelTransformer($this->em, User::class);
|
||||
|
||||
$builder->add(
|
||||
$builder->create(
|
||||
'owner',
|
||||
UserListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.company.field.owner',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'multiple' => false,
|
||||
]
|
||||
)
|
||||
->addModelTransformer($transformer)
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'score',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.company.score',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'scale' => 0,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('projects', ProjectType::class);
|
||||
|
||||
if (!empty($options['update_select'])) {
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'updateSelect',
|
||||
HiddenType::class,
|
||||
[
|
||||
'data' => $options['update_select'],
|
||||
'mapped' => false,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class
|
||||
);
|
||||
}
|
||||
|
||||
if (null === $options['data']->getId()) {
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class
|
||||
);
|
||||
} else {
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'post_extra_buttons' => [
|
||||
[
|
||||
'name' => 'merge',
|
||||
'label' => 'mautic.lead.merge',
|
||||
'attr' => [
|
||||
'class' => 'btn btn-ghost btn-dnd',
|
||||
'icon' => 'ri-building-2-line',
|
||||
'data-toggle' => 'ajaxmodal',
|
||||
'data-target' => '#MauticSharedModal',
|
||||
'data-header' => $this->translator->trans('mautic.lead.company.header.merge'),
|
||||
'href' => $this->router->generate(
|
||||
'mautic_company_action',
|
||||
[
|
||||
'objectId' => $options['data']->getId(),
|
||||
'objectAction' => 'merge',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber($cleaningRules));
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'data_class' => Company::class,
|
||||
'isShortForm' => false,
|
||||
'update_select' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$resolver->setRequired(['fields']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\DBAL\Query\Expression\CompositeExpression;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ConfigCompanyType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'company_unique_identifiers_operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.core.config.contact_unique_identifiers_operator.or' => CompositeExpression::TYPE_OR,
|
||||
'mautic.core.config.contact_unique_identifiers_operator.and' => CompositeExpression::TYPE_AND,
|
||||
],
|
||||
'label' => 'mautic.core.config.unique_identifiers_operator',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.core.config.unique_identifiers_operator.tooltip',
|
||||
],
|
||||
'placeholder' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'companyconfig';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\DBAL\Query\Expression\CompositeExpression;
|
||||
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\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ConfigType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'contact_allow_multiple_companies',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.core.config.allow_multiple_companies',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.core.config.allow_multiple_companies.tooltip',
|
||||
],
|
||||
'data' => (bool) ($options['data']['contact_allow_multiple_companies'] ?? true),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'contact_unique_identifiers_operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.core.config.contact_unique_identifiers_operator.or' => CompositeExpression::TYPE_OR,
|
||||
'mautic.core.config.contact_unique_identifiers_operator.and' => CompositeExpression::TYPE_AND,
|
||||
],
|
||||
'label' => 'mautic.core.config.unique_identifiers_operator',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.core.config.unique_identifiers_operator.tooltip',
|
||||
],
|
||||
'placeholder' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'background_import_if_more_rows_than',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.background.import.if.more.rows.than',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.background.import.if.more.rows.than.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('contact_export_limit', NumberType::class, [
|
||||
'label' => 'mautic.lead.export.limit.rows',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.export.limit.rows.tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
'data' => $options['data']['contact_export_limit'] ?? 0,
|
||||
'constraints' => [
|
||||
new GreaterThanOrEqual([
|
||||
'value' => 0,
|
||||
]),
|
||||
],
|
||||
]);
|
||||
|
||||
$formModifier = static function (FormInterface $form, $currentColumns): void {
|
||||
$order = [];
|
||||
$orderColumns = [];
|
||||
if (!empty($currentColumns)) {
|
||||
$orderColumns = array_values($currentColumns);
|
||||
$order = htmlspecialchars(json_encode($orderColumns), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$form->add(
|
||||
'contact_columns',
|
||||
ContactColumnsType::class,
|
||||
[
|
||||
'label' => 'mautic.config.tab.columns',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control multiselect',
|
||||
'data-sortable' => 'true',
|
||||
'data-order' => $order,
|
||||
],
|
||||
'multiple' => true,
|
||||
'required' => true,
|
||||
'expanded' => false,
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.core.value.required']
|
||||
),
|
||||
],
|
||||
'data'=> array_flip($orderColumns),
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) use ($formModifier): void {
|
||||
$data = $event->getData();
|
||||
$columns = $data['contact_columns'] ?? [];
|
||||
$formModifier($event->getForm(), $columns);
|
||||
}
|
||||
);
|
||||
|
||||
// Build the columns selector
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($formModifier): void {
|
||||
$data = $event->getData();
|
||||
$columns = $data['contact_columns'] ?? [];
|
||||
$formModifier($event->getForm(), $columns);
|
||||
}
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'contact_export_in_background',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.background.export.csv',
|
||||
'data' => $options['data']['contact_export_in_background'] ?? false,
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.lead.background.export.csv.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadconfig';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\LeadBundle\Entity\FrequencyRule;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ContactChannelsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$showContactFrequency = $this->coreParametersHelper->get('show_contact_frequency');
|
||||
$showContactPauseDates = $this->coreParametersHelper->get('show_contact_pause_dates');
|
||||
$showContactPreferredChannels = $this->coreParametersHelper->get('show_contact_preferred_channels');
|
||||
|
||||
$builder->add(
|
||||
'subscribed_channels',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $options['channels'],
|
||||
'expanded' => true,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['onClick' => 'Mautic.togglePreferredChannel(this.value);'],
|
||||
'multiple' => true,
|
||||
'label' => false,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
if (!$options['public_view'] || $showContactPreferredChannels) {
|
||||
$builder->add(
|
||||
'preferred_channel',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $options['channels'],
|
||||
'expanded' => false,
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.lead.list.frequency.preferred.channel',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'placeholder' => false,
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.list.frequency.preferred.channel',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$options['public_view'] || $showContactFrequency || $showContactPauseDates) {
|
||||
foreach ($options['channels'] as $channel) {
|
||||
$attr = (isset($options['data']['subscribed_channels']) && !in_array($channel, $options['data']['subscribed_channels']))
|
||||
? ['disabled' => 'disabled'] : [];
|
||||
|
||||
$builder->add(
|
||||
'frequency_number_'.$channel,
|
||||
IntegerType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.frequency.number',
|
||||
'label_attr' => ['class' => 'text-secondary fw-n label1'],
|
||||
'attr' => array_merge(
|
||||
$attr,
|
||||
[
|
||||
'class' => 'frequency form-control',
|
||||
]
|
||||
),
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'frequency_time_'.$channel,
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.core.time.days' => FrequencyRule::TIME_DAY,
|
||||
'mautic.core.time.weeks' => FrequencyRule::TIME_WEEK,
|
||||
'mautic.core.time.months' => FrequencyRule::TIME_MONTH,
|
||||
],
|
||||
'label' => 'mautic.lead.list.frequency.times',
|
||||
'label_attr' => ['class' => 'text-secondary fw-n frequency-label label2'],
|
||||
'multiple' => false,
|
||||
'required' => false,
|
||||
'attr' => array_merge(
|
||||
$attr,
|
||||
[
|
||||
'class' => 'form-control',
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
if (false == $options['public_view']) {
|
||||
$attributes = array_merge(
|
||||
$attr,
|
||||
[
|
||||
'data-toggle' => 'date',
|
||||
'class' => 'frequency-date form-control',
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$attributes = array_merge(
|
||||
$attr,
|
||||
[
|
||||
'class' => 'form-control',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$options['public_view'] || $showContactPauseDates) {
|
||||
$builder->add(
|
||||
'contact_pause_start_date_'.$channel,
|
||||
DateType::class,
|
||||
$this->configureDateTypeOptions([
|
||||
'widget' => 'single_text',
|
||||
'label' => false,
|
||||
'label_attr' => ['class' => 'text-secondary fw-n label3'],
|
||||
'attr' => $attributes,
|
||||
'required' => false,
|
||||
], $options['public_view'])
|
||||
);
|
||||
$builder->add(
|
||||
'contact_pause_end_date_'.$channel,
|
||||
DateType::class,
|
||||
$this->configureDateTypeOptions([
|
||||
'widget' => 'single_text',
|
||||
'label' => 'mautic.lead.frequency.contact.end.date',
|
||||
'label_attr' => ['class' => 'frequency-label text-secondary fw-n label4'],
|
||||
'attr' => $attributes,
|
||||
'required' => false,
|
||||
], $options['public_view'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['save_button']) && true === $options['save_button']) {
|
||||
$builder->add(
|
||||
'ids',
|
||||
HiddenType::class
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['channels']);
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'public_view' => false,
|
||||
'save_button' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string|bool|mixed[]> $options
|
||||
*
|
||||
* @return array<string,string|bool|mixed[]>
|
||||
*/
|
||||
private function configureDateTypeOptions(array $options, bool $useHtml5): array
|
||||
{
|
||||
$options['html5'] = $useHtml5;
|
||||
|
||||
if (!$useHtml5) {
|
||||
$options['format'] = 'yyyy-MM-dd';
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Services\ContactColumnsDictionary;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ContactColumnsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private ContactColumnsDictionary $columnsDictionary,
|
||||
) {
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'choices' => array_flip($this->columnsDictionary->getFields()),
|
||||
'label' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'required' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ContactFrequencyType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
protected CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$showContactCategories = $this->coreParametersHelper->get('show_contact_categories');
|
||||
$showContactSegments = $this->coreParametersHelper->get('show_contact_segments');
|
||||
|
||||
if (!empty($options['channels'])) {
|
||||
$builder->add(
|
||||
'lead_channels',
|
||||
ContactChannelsType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'channels' => $options['channels'],
|
||||
'data' => $options['data']['lead_channels'],
|
||||
'public_view' => $options['public_view'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$options['public_view']) {
|
||||
$builder->add(
|
||||
'lead_lists',
|
||||
LeadListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.form.list',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'expanded' => $options['public_view'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
} elseif ($showContactSegments) {
|
||||
$builder->add(
|
||||
'lead_lists',
|
||||
LeadListType::class,
|
||||
[
|
||||
'preference_center_only' => $options['preference_center_only'],
|
||||
'label' => 'mautic.lead.form.list',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$options['public_view'] || $showContactCategories) {
|
||||
$builder->add(
|
||||
'global_categories',
|
||||
LeadCategoryType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.form.categories',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => true,
|
||||
'expanded' => $options['public_view'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['channels']);
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'public_view' => false,
|
||||
'preference_center_only' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_contact_frequency_rules';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\PointBundle\Entity\Group;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ContactGroupPointsType extends AbstractType
|
||||
{
|
||||
private const SCORE_FIELD_PREFIX = 'score_group_';
|
||||
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getFieldKey(int $groupId): string
|
||||
{
|
||||
return self::SCORE_FIELD_PREFIX.$groupId;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$pointGroups = $options['point_groups'] ?? [];
|
||||
|
||||
/** @var Group $group */
|
||||
foreach ($pointGroups as $group) {
|
||||
$key = self::getFieldKey($group->getId());
|
||||
$builder->add(
|
||||
$key,
|
||||
IntegerType::class,
|
||||
[
|
||||
'label' => $group->getName(),
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'placeholder' => $this->translator->trans('mautic.point.form.score_not_set'),
|
||||
],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['point_groups']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class DashboardLeadsInTimeWidgetType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'flag',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.filter',
|
||||
'choices' => [
|
||||
'mautic.lead.show.all' => '',
|
||||
'mautic.lead.show.identified' => 'identified',
|
||||
'mautic.lead.show.anonymous' => 'anonymous',
|
||||
'mautic.lead.show.identified.vs.anonymous' => 'identifiedVsAnonymous',
|
||||
'mautic.lead.show.top' => 'top',
|
||||
'mautic.lead.show.top.leads.identified.vs.anonymous' => 'topIdentifiedVsAnonymous',
|
||||
],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'empty_data' => '',
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_dashboard_leads_in_time_widget';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class DashboardLeadsLifetimeWidgetType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private ListModel $segmentModel,
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$lists = $this->segmentModel->getUserLists();
|
||||
$segments = [];
|
||||
$segments[$this->translator->trans('mautic.lead.all.leads')] = 0;
|
||||
foreach ($lists as $list) {
|
||||
$segments[$list['name']] = $list['id'];
|
||||
}
|
||||
|
||||
$builder->add('flag', ChoiceType::class, [
|
||||
'label' => 'mautic.lead.list.filter',
|
||||
'multiple' => true,
|
||||
'choices' => $segments,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_dashboard_leads_lifetime_widget';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class DashboardSegmentsBuildTime extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private ListModel $segmentModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'order',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.core.order',
|
||||
'choices' => [
|
||||
'mautic.widget.segments.build.time.shortest' => 'ASC',
|
||||
'mautic.widget.segments.build.time.longest' => 'DESC',
|
||||
],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'empty_data' => '',
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$lists = $this->segmentModel->getUserLists();
|
||||
$segments = [];
|
||||
foreach ($lists as $list) {
|
||||
$segments[$list['name']] = $list['id'];
|
||||
}
|
||||
|
||||
$builder->add('segments', ChoiceType::class, [
|
||||
'label' => 'mautic.lead.list.filter',
|
||||
'multiple' => true,
|
||||
'choices' => $segments,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\LeadBundle\Entity\LeadDevice;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<LeadDevice>
|
||||
*/
|
||||
class DeviceType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('device', TextType::class);
|
||||
$builder->add('deviceOsName', TextType::class);
|
||||
$builder->add('deviceOsShortName', TextType::class);
|
||||
$builder->add('deviceOsVersion', TextType::class);
|
||||
$builder->add('deviceOsPlatform', TextType::class);
|
||||
$builder->add('deviceModel', TextType::class);
|
||||
$builder->add('deviceBrand', TextType::class);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'data_class' => LeadDevice::class,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leaddevice';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
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\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class DncType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'reason',
|
||||
TextareaType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.dnc_reason',
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'ids',
|
||||
HiddenType::class
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_batch_dnc';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\EmailBundle\Form\Type\EmailListType;
|
||||
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\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class EmailType extends AbstractType
|
||||
{
|
||||
public const REPLY_TO_ADDRESS = 'replyToAddress';
|
||||
|
||||
public function __construct(
|
||||
private UserHelper $userHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber(['body' => 'raw']));
|
||||
|
||||
$builder->add(
|
||||
'subject',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.email.subject',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
]
|
||||
);
|
||||
|
||||
$user = $this->userHelper->getUser();
|
||||
|
||||
$default = (empty($options['data']['fromname'])) ? $user->getFirstName().' '.$user->getLastName() : $options['data']['fromname'];
|
||||
$builder->add(
|
||||
'fromname',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.email.from_name',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-user-6-fill',
|
||||
],
|
||||
'required' => false,
|
||||
'data' => $default,
|
||||
]
|
||||
);
|
||||
|
||||
$default = (empty($options['data']['from'])) ? $user->getEmail() : $options['data']['from'];
|
||||
$builder->add(
|
||||
'from',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.email.from_email',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-mail-line',
|
||||
],
|
||||
'required' => false,
|
||||
'data' => $default,
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'mautic.core.email.required',
|
||||
]),
|
||||
new Email([
|
||||
'message' => 'mautic.core.email.required',
|
||||
]),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
self::REPLY_TO_ADDRESS,
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.email.reply_to_email',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-mail-line',
|
||||
'tooltip' => 'mautic.email.reply_to_email.tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'body',
|
||||
TextareaType::class,
|
||||
[
|
||||
'label' => 'mautic.email.form.body',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control editor editor-basic-fullpage editor-builder-tokens editor-email',
|
||||
'data-token-callback' => 'email:getBuilderTokens',
|
||||
'data-token-activator' => '{',
|
||||
'allow-full-html' => true,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('list', HiddenType::class);
|
||||
|
||||
$builder->add(
|
||||
'templates',
|
||||
EmailListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.email.template',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'onchange' => 'Mautic.getLeadEmailContent(this)',
|
||||
],
|
||||
'multiple' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class, [
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.email.send',
|
||||
'save_class' => 'btn btn-primary',
|
||||
'save_icon' => 'ri-send-plane-line',
|
||||
'cancel_icon' => 'ri-close-line',
|
||||
]);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_quickemail';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\BooleanType;
|
||||
use Mautic\CoreBundle\Form\Type\CountryType;
|
||||
use Mautic\CoreBundle\Form\Type\LocaleType;
|
||||
use Mautic\CoreBundle\Form\Type\LookupType;
|
||||
use Mautic\CoreBundle\Form\Type\MultiselectType;
|
||||
use Mautic\CoreBundle\Form\Type\RegionType;
|
||||
use Mautic\CoreBundle\Form\Type\SelectType;
|
||||
use Mautic\CoreBundle\Form\Type\TimezoneType;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\LeadBundle\Exception\FieldNotFoundException;
|
||||
use Mautic\LeadBundle\Form\FieldAliasToFqcnMap;
|
||||
use Mautic\LeadBundle\Form\Validator\Constraints\EmailAddress;
|
||||
use Mautic\LeadBundle\Helper\FormFieldHelper;
|
||||
use Mautic\LeadBundle\Validator\Constraints\Length;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TimeType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
trait EntityFieldsBuildFormTrait
|
||||
{
|
||||
/**
|
||||
* @return array<string, 'html'|'raw'>
|
||||
*/
|
||||
private function getFormFields(FormBuilderInterface $builder, array $options, $object = 'lead'): array
|
||||
{
|
||||
$cleaningRules = [];
|
||||
$fieldValues = [];
|
||||
$isObject = false;
|
||||
if (!empty($options['data'])) {
|
||||
$isObject = is_object($options['data']);
|
||||
$fieldValues = ($isObject) ? $options['data']->getFields() : $options['data'];
|
||||
}
|
||||
$mapped = !$isObject;
|
||||
|
||||
foreach ($options['fields'] as $field) {
|
||||
if (false === $field['isPublished'] || $field['object'] !== $object) {
|
||||
continue;
|
||||
}
|
||||
$attr = ['class' => 'form-control'];
|
||||
$properties = $field['properties'];
|
||||
$type = $field['type'];
|
||||
$required = ($isObject) ? $field['isRequired'] : false;
|
||||
$alias = $field['alias'];
|
||||
$group = $field['group'];
|
||||
|
||||
try {
|
||||
$type = FieldAliasToFqcnMap::getFqcn($type);
|
||||
} catch (FieldNotFoundException) {
|
||||
}
|
||||
|
||||
if ($field['isUniqueIdentifer']) {
|
||||
$attr['data-unique-identifier'] = $field['alias'];
|
||||
}
|
||||
|
||||
if ($isObject) {
|
||||
$value = $fieldValues[$group][$alias]['value'] ?? $field['defaultValue'];
|
||||
} else {
|
||||
$value = $fieldValues[$alias] ?? '';
|
||||
}
|
||||
|
||||
$constraints = [];
|
||||
if ($required && empty($options['ignore_required_constraints'])) {
|
||||
$constraints[] = new NotBlank(
|
||||
['message' => 'mautic.lead.customfield.notblank']
|
||||
);
|
||||
} elseif (!empty($options['ignore_required_constraints'])) {
|
||||
$required = false;
|
||||
$field['isRequired'] = false;
|
||||
}
|
||||
|
||||
if ($field['charLengthLimit'] > 0) {
|
||||
$constraints[] = new Length(['max' => $field['charLengthLimit']]);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case NumberType::class:
|
||||
if (empty($properties['scale'])) {
|
||||
$properties['scale'] = null;
|
||||
} // ensure default locale is used
|
||||
else {
|
||||
$properties['scale'] = (int) $properties['scale'];
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
// Prevent transform errors
|
||||
$value = null;
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
$alias,
|
||||
$type,
|
||||
[
|
||||
'required' => $required,
|
||||
'label' => $field['label'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => $attr,
|
||||
'data' => (null !== $value) ? (float) $value : $value,
|
||||
'mapped' => $mapped,
|
||||
'constraints' => $constraints,
|
||||
'scale' => $properties['scale'],
|
||||
'rounding_mode' => isset($properties['roundmode']) ? (int) $properties['roundmode'] : 0,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case DateType::class:
|
||||
case DateTimeType::class:
|
||||
case TimeType::class:
|
||||
$opts = [
|
||||
'required' => $required,
|
||||
'label' => $field['label'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => $attr,
|
||||
'mapped' => $mapped,
|
||||
'constraints' => $constraints,
|
||||
];
|
||||
|
||||
if (!empty($options['ignore_date_type'])) {
|
||||
$type = TextType::class;
|
||||
} else {
|
||||
$opts['html5'] = false;
|
||||
$opts['input'] = 'string';
|
||||
$opts['widget'] = 'single_text';
|
||||
$opts['html5'] = false;
|
||||
if ($value) {
|
||||
try {
|
||||
$dtHelper = new DateTimeHelper($value, null, 'local');
|
||||
} catch (\Exception) {
|
||||
// Rather return empty value than break the page
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
if (DateTimeType::class === $type) {
|
||||
$opts['attr']['data-toggle'] = 'datetime';
|
||||
$opts['model_timezone'] = 'UTC';
|
||||
$opts['view_timezone'] = date_default_timezone_get();
|
||||
$opts['format'] = 'yyyy-MM-dd HH:mm:ss';
|
||||
$opts['with_seconds'] = true;
|
||||
|
||||
$opts['data'] = (!empty($value)) ? $dtHelper->toLocalString('Y-m-d H:i:s') : null;
|
||||
} elseif (DateType::class === $type) {
|
||||
$opts['attr']['data-toggle'] = 'date';
|
||||
$opts['data'] = (!empty($value)) ? $dtHelper->toLocalString('Y-m-d') : null;
|
||||
} else {
|
||||
$opts['attr']['data-toggle'] = 'time';
|
||||
// $opts['with_seconds'] = true; // @todo figure out why this cause the contact form to fail.
|
||||
$opts['data'] = (!empty($value)) ? $dtHelper->toLocalString('H:i:s') : null;
|
||||
}
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($alias, $type): void {
|
||||
$data = $event->getData();
|
||||
|
||||
if (!empty($data[$alias])) {
|
||||
if (false === ($timestamp = strtotime($data[$alias]))) {
|
||||
$timestamp = null;
|
||||
}
|
||||
if ($timestamp) {
|
||||
$dtHelper = new DateTimeHelper(date('Y-m-d H:i:s', $timestamp), null, 'local');
|
||||
switch ($type) {
|
||||
case DateTimeType::class:
|
||||
$data[$alias] = $dtHelper->toLocalString('Y-m-d H:i:s');
|
||||
break;
|
||||
case DateType::class:
|
||||
$data[$alias] = $dtHelper->toLocalString('Y-m-d');
|
||||
break;
|
||||
case TimeType::class:
|
||||
$data[$alias] = $dtHelper->toLocalString('H:i:s');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$event->setData($data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$builder->add($alias, $type, $opts);
|
||||
break;
|
||||
case SelectType::class:
|
||||
case MultiselectType::class:
|
||||
case BooleanType::class:
|
||||
if (MultiselectType::class === $type) {
|
||||
$constraints[] = new Length(['max' => 65535]);
|
||||
}
|
||||
|
||||
$typeProperties = [
|
||||
'required' => $required,
|
||||
'label' => $field['label'],
|
||||
'attr' => $attr,
|
||||
'mapped' => $mapped,
|
||||
'constraints' => $constraints,
|
||||
];
|
||||
|
||||
$emptyValue = '';
|
||||
|
||||
if (array_key_exists('use_nullable_yes_no_type', $options) && true === $options['use_nullable_yes_no_type'] && BooleanType::class === $type) {
|
||||
$type = NullableYesNoButtonGroupType::class;
|
||||
$emptyValue = 'mautic.core.form.no_change';
|
||||
} elseif (in_array($type, [SelectType::class, MultiselectType::class]) && !empty($properties['list'])) {
|
||||
$typeProperties['choices'] = array_flip(FormFieldHelper::parseList($properties['list']));
|
||||
$cleaningRules[$field['alias']] = 'raw';
|
||||
}
|
||||
if (BooleanType::class === $type && !empty($properties['yes']) && !empty($properties['no'])) {
|
||||
$typeProperties['yes_label'] = $properties['yes'];
|
||||
$typeProperties['no_label'] = $properties['no'];
|
||||
$emptyValue = ' x ';
|
||||
if ('' !== $value && null !== $value) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
}
|
||||
|
||||
$typeProperties['data'] = MultiselectType::class === $type ? FormFieldHelper::parseList($value) : $value;
|
||||
$typeProperties['placeholder'] = $emptyValue;
|
||||
$builder->add(
|
||||
$alias,
|
||||
$type,
|
||||
$typeProperties
|
||||
);
|
||||
break;
|
||||
case CountryType::class:
|
||||
case RegionType::class:
|
||||
case TimezoneType::class:
|
||||
case LocaleType::class:
|
||||
$builder->add(
|
||||
$alias,
|
||||
$type,
|
||||
[
|
||||
'required' => $required,
|
||||
'label' => $field['label'],
|
||||
'data' => $value,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-placeholder' => $field['label'],
|
||||
],
|
||||
'mapped' => $mapped,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$attr['data-encoding'] = 'raw';
|
||||
switch ($type) {
|
||||
case LookupType::class:
|
||||
$attr['data-target'] = $alias;
|
||||
$constraints[] = new Length(['max' => 191]);
|
||||
if (!empty($properties['list'])) {
|
||||
$attr['data-options'] = FormFieldHelper::formatList(FormFieldHelper::FORMAT_BAR, array_keys(FormFieldHelper::parseList($properties['list'])));
|
||||
}
|
||||
break;
|
||||
case EmailType::class:
|
||||
// Enforce a valid email
|
||||
$attr['data-encoding'] = 'email';
|
||||
$constraints[] = new EmailAddress();
|
||||
break;
|
||||
case TextType::class:
|
||||
$constraints[] = new Length(['max' => 191]);
|
||||
break;
|
||||
|
||||
case MultiselectType::class:
|
||||
$constraints[] = new Length(['max' => 65535]);
|
||||
break;
|
||||
case HtmlType::class:
|
||||
$cleaningRules[$field['alias']] = 'html';
|
||||
break;
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
$alias,
|
||||
$type,
|
||||
[
|
||||
'required' => $field['isRequired'],
|
||||
'label' => $field['label'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => $attr,
|
||||
'data' => $value,
|
||||
'mapped' => $mapped,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $cleaningRules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Mautic\CoreBundle\Form\EventListener\FormExitSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\SortableListType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Entity\LeadFieldRepository;
|
||||
use Mautic\LeadBundle\Field\Helper\IndexHelper;
|
||||
use Mautic\LeadBundle\Field\IdentifierFields;
|
||||
use Mautic\LeadBundle\Field\SchemaDefinition;
|
||||
use Mautic\LeadBundle\Form\DataTransformer\FieldToOrderTransformer;
|
||||
use Mautic\LeadBundle\Helper\FormFieldHelper;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
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\Form\FormInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Constraints\IsFalse;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<LeadField>
|
||||
*/
|
||||
class FieldType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private static array $fieldsWithNoLengthLimit = [
|
||||
'textarea',
|
||||
'html',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private EntityManagerInterface $em,
|
||||
private Translator $translator,
|
||||
private IdentifierFields $identifierFields,
|
||||
private IndexHelper $indexHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventSubscriber(new FormExitSubscriber('lead.field', $options));
|
||||
|
||||
$builder->add(
|
||||
'label',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.label',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control', 'length' => 191],
|
||||
]
|
||||
);
|
||||
|
||||
$disabled = (!empty($options['data'])) ? $options['data']->isFixed() : false;
|
||||
|
||||
$builder->add(
|
||||
'group',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.lead.field.group.core' => 'core',
|
||||
'mautic.lead.field.group.social' => 'social',
|
||||
'mautic.lead.field.group.personal' => 'personal',
|
||||
'mautic.lead.field.group.professional' => 'professional',
|
||||
],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.field.form.group.help',
|
||||
'onchange' => 'Mautic.updateLeadFieldOrderChoiceList();',
|
||||
],
|
||||
'expanded' => false,
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.lead.field.group',
|
||||
'placeholder' => false,
|
||||
'required' => false,
|
||||
'disabled' => $disabled,
|
||||
]
|
||||
);
|
||||
|
||||
$new = $options['data']->getId() ? false : true;
|
||||
$type = $options['data']->getType();
|
||||
$isIndex = $options['data']->isIsIndex();
|
||||
$default = (empty($type)) ? 'text' : $type;
|
||||
$fieldHelper = new FormFieldHelper();
|
||||
$fieldHelper->setTranslator($this->translator);
|
||||
|
||||
$builder->add(
|
||||
'type',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $fieldHelper->getChoiceList(),
|
||||
'expanded' => false,
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.lead.field.type',
|
||||
'placeholder' => false,
|
||||
'disabled' => ($disabled || !$new),
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'onchange' => 'Mautic.updateLeadFieldProperties(this.value);',
|
||||
],
|
||||
'data' => $default,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'properties_select_template',
|
||||
SortableListType::class,
|
||||
[
|
||||
'mapped' => false,
|
||||
'label' => 'mautic.lead.field.form.properties.select',
|
||||
'option_required' => false,
|
||||
'with_labels' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'properties_lookup_template',
|
||||
SortableListType::class,
|
||||
[
|
||||
'mapped' => false,
|
||||
'label' => 'mautic.lead.field.form.properties.select',
|
||||
'option_required' => false,
|
||||
'with_labels' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$listChoices = [
|
||||
'country' => FormFieldHelper::getCountryChoices(),
|
||||
'region' => FormFieldHelper::getRegionChoices(),
|
||||
'timezone' => FormFieldHelper::getTimezonesChoices(),
|
||||
'locale' => FormFieldHelper::getLocaleChoices(),
|
||||
'select' => [],
|
||||
];
|
||||
foreach ($listChoices as $listType => $choices) {
|
||||
$builder->add(
|
||||
'default_template_'.$listType,
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $choices,
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control not-chosen'],
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'default_template_text',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'default_template_textarea',
|
||||
TextareaType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'default_template_boolean',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
'data' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'properties',
|
||||
CollectionType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'allow_add' => true,
|
||||
'error_bubbling' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$disableDefaultValue = (!$new && in_array($options['data']->getAlias(), $this->identifierFields->getFieldList($options['data']->getObject())));
|
||||
$builder->add(
|
||||
'defaultValue',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.field.help.defaultvalue',
|
||||
],
|
||||
'required' => false,
|
||||
'disabled' => $disableDefaultValue,
|
||||
'constraints' => [
|
||||
new Assert\Callback([$this, 'validateDefaultValue']),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* @see FormEvents::PRE_SET_DATA
|
||||
* Used as as form modifier before trying to set data
|
||||
*/
|
||||
$formModifier = function (FormEvent $event) use ($listChoices, $type, $options, $disableDefaultValue): array {
|
||||
$cleaningRules = [];
|
||||
$form = $event->getForm();
|
||||
$data = $event->getData();
|
||||
$type = (is_array($data)) ? ($data['type'] ?? $type) : $data->getType();
|
||||
$constraints = [];
|
||||
|
||||
switch ($type) {
|
||||
case 'select':
|
||||
case 'lookup':
|
||||
$constraints = new Assert\Callback([$this, 'validateDefaultValue']);
|
||||
// no break
|
||||
case 'multiselect':
|
||||
$cleaningRules['defaultValue'] = 'raw';
|
||||
|
||||
if (is_array($data)) {
|
||||
$properties = $data['properties'] ?? [];
|
||||
} else {
|
||||
$properties = $data->getProperties();
|
||||
}
|
||||
|
||||
$propertiesList['list'] = isset($properties['list']) && 'lookup' === $type ? array_flip(array_filter($properties['list'])) : $properties['list'];
|
||||
|
||||
$form->add(
|
||||
'properties',
|
||||
SortableListType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => 'mautic.lead.field.form.properties.select',
|
||||
'data' => $propertiesList,
|
||||
'with_labels' => ('lookup' !== $type),
|
||||
'option_constraint' => [],
|
||||
]
|
||||
);
|
||||
|
||||
$list = isset($properties['list']) ? FormFieldHelper::parseList($properties['list']) : [];
|
||||
$form->add(
|
||||
'defaultValue',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label is-chosen'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'choices' => array_flip($list),
|
||||
'multiple' => 'multiselect' === $type,
|
||||
'data' => 'multiselect' === $type && is_string($options['data']->getDefaultValue()) ? explode('|', $options['data']->getDefaultValue()) : $options['data']->getDefaultValue(),
|
||||
'disabled' => $disableDefaultValue,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'country':
|
||||
case 'locale':
|
||||
case 'timezone':
|
||||
case 'region':
|
||||
$form->add(
|
||||
'defaultValue',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $listChoices[$type],
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'disabled' => $disableDefaultValue,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'boolean':
|
||||
if (is_array($data)) {
|
||||
$value = $data['defaultValue'] ?? false;
|
||||
$yesLabel = !empty($data['properties']['yes']) ? $data['properties']['yes'] : 'mautic.core.form.yes';
|
||||
$noLabel = !empty($data['properties']['no']) ? $data['properties']['no'] : 'mautic.core.form.no';
|
||||
} else {
|
||||
$value = $data->getDefaultValue();
|
||||
$props = $data->getProperties();
|
||||
$yesLabel = !empty($props['yes']) ? $props['yes'] : 'mautic.core.form.yes';
|
||||
$noLabel = !empty($props['no']) ? $props['no'] : 'mautic.core.form.no';
|
||||
}
|
||||
|
||||
if ('' !== $value && null !== $value) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'defaultValue',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'data' => $value,
|
||||
'no_label' => $noLabel,
|
||||
'yes_label' => $yesLabel,
|
||||
'placeholder' => ' x ',
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'datetime':
|
||||
case 'date':
|
||||
case 'time':
|
||||
$constraints = [];
|
||||
switch ($type) {
|
||||
case 'datetime':
|
||||
$constraints = [
|
||||
new Assert\Callback(
|
||||
function ($object, ExecutionContextInterface $context): void {
|
||||
if (!empty($object) && false === \DateTime::createFromFormat('Y-m-d H:i', $object)) {
|
||||
$context->buildViolation('mautic.lead.datetime.invalid')->addViolation();
|
||||
}
|
||||
}
|
||||
),
|
||||
];
|
||||
break;
|
||||
case 'date':
|
||||
$constraints = [
|
||||
new Assert\Callback(
|
||||
function ($object, ExecutionContextInterface $context): void {
|
||||
if (!empty($object)) {
|
||||
$validator = $context->getValidator();
|
||||
$violations = $validator->validate($object, new Assert\Date());
|
||||
|
||||
if (count($violations) > 0) {
|
||||
$context->buildViolation('mautic.lead.date.invalid')->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
];
|
||||
break;
|
||||
case 'time':
|
||||
$constraints = [
|
||||
new Assert\Callback(
|
||||
function ($object, ExecutionContextInterface $context): void {
|
||||
if (!empty($object)) {
|
||||
$validator = $context->getValidator();
|
||||
$violations = $validator->validate(
|
||||
$object,
|
||||
new Assert\Regex(['pattern' => '/(2[0-3]|[01][0-9]):([0-5][0-9])/'])
|
||||
);
|
||||
|
||||
if (count($violations) > 0) {
|
||||
$context->buildViolation('mautic.lead.time.invalid')->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'defaultValue',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => $type,
|
||||
],
|
||||
'required' => false,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'tel':
|
||||
case 'url':
|
||||
case 'email':
|
||||
$constraints = new Assert\Callback([$this, 'validateDefaultValue']);
|
||||
// no break
|
||||
case 'number':
|
||||
$form->add(
|
||||
'defaultValue',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.defaultvalue',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'type' => $type,
|
||||
],
|
||||
'required' => false,
|
||||
'disabled' => $disableDefaultValue,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (in_array($type, LeadField::TYPES_SUPPORTING_LENGTH)) {
|
||||
$this->addLengthValidationField($form);
|
||||
}
|
||||
|
||||
return $cleaningRules;
|
||||
};
|
||||
|
||||
$setupOrderField = function (FormInterface $form, ?string $object = null, ?string $group = null) use ($builder, $disabled): void {
|
||||
/** @var LeadFieldRepository $leadFieldRepository */
|
||||
$leadFieldRepository = $this->em->getRepository(LeadField::class);
|
||||
|
||||
$options = [
|
||||
'label' => 'mautic.core.order.field',
|
||||
'class' => LeadField::class,
|
||||
'choice_label' => 'label',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => $disabled ? 'mautic.core.order.field.tooltip.disabled' : 'mautic.core.order.field.tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
'auto_initialize' => false,
|
||||
'disabled' => $disabled,
|
||||
];
|
||||
// There's no need to filter list during FormEvents::PRE_SUBMIT.
|
||||
if ($object && $group) {
|
||||
$options['query_builder'] = fn (EntityRepository $er) => $er->createQueryBuilder('f')
|
||||
->orderBy('f.order', Order::Ascending->value)
|
||||
->where('f.object = :object')
|
||||
->setParameter('object', $object)
|
||||
->andWhere('f.group = :group')
|
||||
->setParameter('group', $group)
|
||||
->andWhere('f.isFixed = FALSE');
|
||||
}
|
||||
|
||||
// get order list
|
||||
$transformer = new FieldToOrderTransformer($leadFieldRepository);
|
||||
$form->add(
|
||||
$builder->create(
|
||||
'order',
|
||||
EntityType::class,
|
||||
$options,
|
||||
)->addModelTransformer($transformer)->getForm()
|
||||
);
|
||||
};
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SET_DATA,
|
||||
function (FormEvent $event) use ($formModifier, $setupOrderField): void {
|
||||
$formModifier($event);
|
||||
/** @var LeadField $field */
|
||||
$field = $event->getData();
|
||||
$setupOrderField($event->getForm(), $field->getObject(), $field->getGroup());
|
||||
}
|
||||
);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($formModifier, $disableDefaultValue, $setupOrderField): void {
|
||||
$data = $event->getData();
|
||||
$cleaningRules = $formModifier($event);
|
||||
$masks = !empty($cleaningRules) ? $cleaningRules : 'clean';
|
||||
// clean the data
|
||||
$data = InputHelper::_($data, $masks);
|
||||
|
||||
if ((isset($data['group']) && 'social' === $data['group']) || !empty($data['isUniqueIdentifer']) || $disableDefaultValue) {
|
||||
// Don't allow a default for social or unique identifiers
|
||||
$data['defaultValue'] = null;
|
||||
}
|
||||
|
||||
if (isset($data['type']) && !in_array($data['type'], LeadField::TYPES_SUPPORTING_LENGTH)) {
|
||||
$data['charLengthLimit'] = null;
|
||||
}
|
||||
|
||||
$event->setData($data);
|
||||
$setupOrderField($event->getForm());
|
||||
}
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'alias',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.alias',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'length' => 25,
|
||||
'tooltip' => 'mautic.lead.field.help.alias',
|
||||
],
|
||||
'required' => false,
|
||||
'disabled' => ($disabled || !$new),
|
||||
]
|
||||
);
|
||||
|
||||
$attr = [];
|
||||
if ($options['data']->getColumnIsNotCreated()) {
|
||||
$attr = [
|
||||
'tooltip' => 'mautic.lead.field.being_created_in_background',
|
||||
];
|
||||
}
|
||||
|
||||
if ($options['data']->getColumnIsNotRemoved()) {
|
||||
if (array_key_exists('tooltip', $attr)) {
|
||||
$attr['tooltip'] = $attr['tooltip'].' mautic.lead.field.being_removed_in_background';
|
||||
} else {
|
||||
$attr['tooltip'] = 'mautic.lead.field.being_removed_in_background';
|
||||
}
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'isPublished',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'disabled' => $options['data']->disablePublishChange(),
|
||||
'attr' => $attr,
|
||||
'data' => ('email' == $options['data']->getAlias()) ? true : $options['data']->getIsPublished(),
|
||||
'label' => 'mautic.core.form.available',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isRequired',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.core.required',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isVisible',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.form.isvisible',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isShortVisible',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.form.isshortvisible',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.lead.field.form.isshortvisible.tooltip',
|
||||
'data-disable-on' => '{"leadfield_object":"company"}',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isListable',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.form.islistable',
|
||||
]
|
||||
);
|
||||
|
||||
$constraints = [];
|
||||
|
||||
if (false === $options['data']->isIsindex() && false === $this->indexHelper->isNewIndexAllowed()) {
|
||||
$constraints[] = new IsFalse(['message' => 'mautic.lead.field.form.index_count.error']);
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'isIndex',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.indexable',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'yes_label' => 'mautic.lead.field.indexable.yes',
|
||||
'no_label' => 'mautic.lead.field.indexable.no',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => $this->translator->trans('mautic.lead.field.form.isIndex.tooltip', ['%indexCount%' => $this->indexHelper->getIndexCount(), '%maxCount%' => $this->indexHelper->getMaxCount()]),
|
||||
'readonly'=> (false === $isIndex && $this->indexHelper->getIndexCount() >= $this->indexHelper->getMaxCount()),
|
||||
],
|
||||
'required' => false,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
|
||||
$data = $options['data']->isUniqueIdentifier();
|
||||
$builder->add(
|
||||
'isUniqueIdentifer',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.form.isuniqueidentifer',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.lead.field.form.isuniqueidentifer.tooltip',
|
||||
'onchange' => 'Mautic.displayUniqueIdentifierWarning(this);',
|
||||
'data-disable-on' => '{"leadfield_object":"company"}',
|
||||
],
|
||||
'data' => (!empty($data)),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isPubliclyUpdatable',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.form.ispubliclyupdatable',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.lead.field.form.ispubliclyupdatable.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'object',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => [
|
||||
'mautic.lead.contact' => 'lead',
|
||||
'mautic.company.company' => 'company',
|
||||
],
|
||||
'expanded' => false,
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.lead.field.object',
|
||||
'placeholder' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'onchange' => 'Mautic.updateLeadFieldOrderChoiceList();',
|
||||
],
|
||||
'required' => false,
|
||||
'disabled' => ($disabled || !$new),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'data_class' => LeadField::class,
|
||||
'validation_groups' => function (FormInterface $form): array {
|
||||
$data = $form->getData();
|
||||
\assert($data instanceof LeadField);
|
||||
|
||||
$groups = ['Default'];
|
||||
|
||||
if ($data->supportsLength()) {
|
||||
$groups[] = 'indexableFieldWithLimits';
|
||||
}
|
||||
|
||||
return $groups;
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadfield';
|
||||
}
|
||||
|
||||
public static function validateDefaultValue(?string $value, ExecutionContextInterface $context): void
|
||||
{
|
||||
if (empty($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var LeadField $field */
|
||||
$field = $context->getRoot()->getViewData();
|
||||
|
||||
if (in_array($field->getType(), self::$fieldsWithNoLengthLimit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$limit = $field->getCharLengthLimit();
|
||||
$defaultValueLength = mb_strlen($value);
|
||||
|
||||
if ($defaultValueLength <= $limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translationParameters = [
|
||||
'%currentLength%' => $defaultValueLength,
|
||||
'%defaultValueLengthLimit%' => $limit,
|
||||
];
|
||||
|
||||
$context
|
||||
->buildViolation('mautic.lead.defaultValue.maxlengthexceeded', $translationParameters)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
private function addLengthValidationField(FormInterface $form): void
|
||||
{
|
||||
$typesWithMaxLength = implode('","', LeadField::TYPES_SUPPORTING_LENGTH);
|
||||
|
||||
$form->add(
|
||||
'charLengthLimit',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.field.form.maximum.character.length',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-show-on' => '{
|
||||
"leadfield_type":["'.$typesWithMaxLength.'"]
|
||||
}',
|
||||
],
|
||||
'constraints' => [
|
||||
new Assert\NotBlank(['groups' => 'indexableFieldWithLimits']),
|
||||
new Assert\Range(['min' => 1, 'max' => SchemaDefinition::MAX_VARCHAR_LENGTH, 'groups' => 'indexableFieldWithLimits']),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* This form is filled with the LeadEvents::ADJUST_FILTER_FORM_TYPE_FOR_FIELD subscribers.
|
||||
*
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class FilterPropertiesType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
// This form is loaded via AJAX as part of another form.
|
||||
// Disable CSRF protection to avoid validation errors with unexpected fileds.
|
||||
$resolver->setDefaults(['csrf_protection' => false]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Mautic\LeadBundle\Entity\RegexTrait;
|
||||
use Mautic\LeadBundle\Helper\FormFieldHelper;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
trait FilterTrait
|
||||
{
|
||||
use RegexTrait;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
public function setConnection(Connection $connection): void
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $eventName
|
||||
*/
|
||||
public function buildFiltersForm($eventName, FormEvent $event, TranslatorInterface $translator, $currentListId = null): void
|
||||
{
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
$options = $form->getConfig()->getOptions();
|
||||
|
||||
if (!isset($data['type'])) {
|
||||
$data['type'] = TextType::class;
|
||||
$data['field'] = '';
|
||||
$data['operator'] = null;
|
||||
}
|
||||
|
||||
$fieldType = $data['type'];
|
||||
$fieldName = $data['field'];
|
||||
$type = TextType::class;
|
||||
$attr = ['class' => 'form-control filter-value'];
|
||||
$displayType = HiddenType::class;
|
||||
$displayAttr = [];
|
||||
$operator = $data['operator'] ?? '';
|
||||
$field = [];
|
||||
|
||||
if (isset($options['fields']['behaviors'][$fieldName])) {
|
||||
$field = $options['fields']['behaviors'][$fieldName];
|
||||
} elseif (isset($data['object']) && isset($options['fields'][$data['object']][$fieldName])) {
|
||||
$field = $options['fields'][$data['object']][$fieldName];
|
||||
}
|
||||
|
||||
$customOptions = [];
|
||||
switch ($fieldType) {
|
||||
case 'assets':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['assets'];
|
||||
$customOptions['multiple'] = true;
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'leadlist':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
// Don't show the current list ID in the choices
|
||||
if (!empty($currentListId)) {
|
||||
unset($options['lists'][$currentListId]);
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['lists'];
|
||||
$customOptions['multiple'] = in_array($data['operator'], ['in', '!in']);
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'campaign':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['campaign'];
|
||||
$customOptions['multiple'] = true;
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'lead_email_received':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['emails'];
|
||||
$customOptions['multiple'] = true;
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'device_type':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['deviceTypes'];
|
||||
$customOptions['multiple'] = true;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'device_brand':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['deviceBrands'];
|
||||
$customOptions['multiple'] = true;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'device_os':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $options['deviceOs'];
|
||||
$customOptions['multiple'] = true;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'tags':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
$customOptions['choices'] = $options['tags'];
|
||||
$customOptions['multiple'] = true;
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
$attr = array_merge(
|
||||
$attr,
|
||||
[
|
||||
'data-placeholder' => $translator->trans('mautic.lead.tags.select_or_create'),
|
||||
'data-no-results-text' => $translator->trans('mautic.lead.tags.enter_to_create'),
|
||||
'data-allow-add' => 'true',
|
||||
'onchange' => 'Mautic.createLeadTag(this)',
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'stage':
|
||||
$customOptions['choices'] = $options['stage'];
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'globalcategory':
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
$customOptions['choices'] = $options['globalcategory'];
|
||||
$customOptions['multiple'] = true;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'timezone':
|
||||
case 'country':
|
||||
case 'region':
|
||||
case 'locale':
|
||||
switch ($fieldType) {
|
||||
case 'timezone':
|
||||
$choiceKey = 'timezones';
|
||||
break;
|
||||
case 'country':
|
||||
$choiceKey = 'countries';
|
||||
break;
|
||||
case 'region':
|
||||
$choiceKey = 'regions';
|
||||
break;
|
||||
case 'locale':
|
||||
$choiceKey = 'locales';
|
||||
break;
|
||||
}
|
||||
|
||||
$type = ChoiceType::class;
|
||||
$customOptions['choices'] = $options[$choiceKey];
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$customOptions['multiple'] = in_array($operator, ['in', '!in']);
|
||||
|
||||
if ($customOptions['multiple']) {
|
||||
array_unshift($customOptions['choices'], ['' => '']);
|
||||
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'time':
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
$attr['data-toggle'] = $fieldType;
|
||||
break;
|
||||
case 'lookup_id':
|
||||
$type = HiddenType::class;
|
||||
$displayType = TextType::class;
|
||||
$displayAttr = array_merge(
|
||||
$displayAttr,
|
||||
[
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => 'field-lookup',
|
||||
'data-target' => $data['field'],
|
||||
'data-action' => $field['properties']['data-action'] ?? 'lead:fieldList',
|
||||
'data-lookup-callback' => $field['properties']['data-lookup-callback'] ?? 'updateLookupListFilter',
|
||||
'data-callback' => $field['properties']['callback'] ?? 'activateFieldTypeahead',
|
||||
'placeholder' => $translator->trans(
|
||||
'mautic.lead.list.form.filtervalue'
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
if (isset($field['properties']['list'])) {
|
||||
$displayAttr['data-options'] = $field['properties']['list'];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'select':
|
||||
case 'multiselect':
|
||||
case 'boolean':
|
||||
$attr = array_merge(
|
||||
$attr,
|
||||
[
|
||||
'placeholder' => $translator->trans('mautic.lead.list.form.filtervalue'),
|
||||
]
|
||||
);
|
||||
|
||||
if (in_array($operator, ['in', '!in'])) {
|
||||
$customOptions['multiple'] = true;
|
||||
if (!isset($data['filter'])) {
|
||||
$data['filter'] = [];
|
||||
} elseif (!is_array($data['filter'])) {
|
||||
$data['filter'] = [$data['filter']];
|
||||
}
|
||||
}
|
||||
|
||||
$choices = [];
|
||||
if (!empty($field['properties']['list'])) {
|
||||
$list = $field['properties']['list'];
|
||||
$choices = ('boolean' === $fieldType)
|
||||
? FormFieldHelper::parseBooleanList($list)
|
||||
: FormFieldHelper::parseList($list);
|
||||
}
|
||||
|
||||
if ('select' === $fieldType) {
|
||||
// array_unshift cannot be used because numeric values get lost as keys
|
||||
$choices = array_reverse($choices, true);
|
||||
$choices[''] = '';
|
||||
$choices = array_reverse($choices, true);
|
||||
}
|
||||
|
||||
$customOptions['choices'] = $choices;
|
||||
$customOptions['choice_translation_domain'] = false;
|
||||
$type = ChoiceType::class;
|
||||
break;
|
||||
case 'lookup':
|
||||
$attr = array_merge(
|
||||
$attr,
|
||||
[
|
||||
'data-toggle' => 'field-lookup',
|
||||
'data-target' => $data['field'] ?? '',
|
||||
'data-action' => 'lead:fieldList',
|
||||
'placeholder' => $translator->trans('mautic.lead.list.form.filtervalue'),
|
||||
]
|
||||
);
|
||||
|
||||
if (isset($field['properties']['list'])) {
|
||||
$attr['data-options'] = $field['properties']['list'];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$customOptions['constraints'] = [];
|
||||
if (in_array($operator, ['empty', '!empty'])) {
|
||||
$attr['disabled'] = 'disabled';
|
||||
} elseif ($operator) {
|
||||
$customOptions['constraints'][] = new NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
);
|
||||
|
||||
if (in_array($operator, ['regexp', '!regexp']) && $this->connection) {
|
||||
// Let's add a custom valdiator to test the regex
|
||||
$customOptions['constraints'][] =
|
||||
new Callback(
|
||||
function ($regex, ExecutionContextInterface $context): void {
|
||||
// Let's test the regex's syntax by making a fake query
|
||||
try {
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->select('l.id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'leads', 'l')
|
||||
->where('l.id REGEXP :regex')
|
||||
->setParameter('regex', $this->prepareRegex($regex))
|
||||
->setMaxResults(1);
|
||||
$qb->executeQuery()->fetchAllAssociative();
|
||||
} catch (\Exception) {
|
||||
$context->buildViolation('mautic.core.regex.invalid')->addViolation();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @todo implement in UI
|
||||
if (in_array($operator, ['between', '!between'])) {
|
||||
$form->add(
|
||||
'filter',
|
||||
CollectionType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'entry_type' => $type,
|
||||
'entry_options' => [
|
||||
'label' => false,
|
||||
'attr' => $attr,
|
||||
],
|
||||
]
|
||||
);
|
||||
} else {
|
||||
foreach ($customOptions['constraints'] as $i => $constraint) {
|
||||
if (NotBlank::class === $constraint::class) {
|
||||
array_splice($customOptions['constraints'], $i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($data['operator'], ['empty', '!empty'])) {
|
||||
// @see Symfony\Component\Form\Extension\Core\Type\ChoiceType::configureOptions
|
||||
$data['filter'] = null;
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'filter',
|
||||
$type,
|
||||
array_merge(
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => $attr,
|
||||
'data' => $data['filter'] ?? '',
|
||||
'error_bubbling' => false,
|
||||
],
|
||||
$customOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'display',
|
||||
$displayType,
|
||||
[
|
||||
'label' => false,
|
||||
'attr' => $displayAttr,
|
||||
'data' => $data['display'] ?? '',
|
||||
'error_bubbling' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$form->add(
|
||||
'operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'choices' => $field['operators'] ?? [],
|
||||
'attr' => [
|
||||
'class' => 'form-control not-chosen filter-operator',
|
||||
'onchange' => 'Mautic.convertDwcFilterInput(this)',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (FormEvents::PRE_SUBMIT === $eventName) {
|
||||
$event->setData($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Mautic\LeadBundle\Provider\FormAdjustmentsProviderInterface;
|
||||
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\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class FilterType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private FormAdjustmentsProviderInterface $formAdjustmentsProvider,
|
||||
private ListModel $listModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$fieldChoices = $this->listModel->getChoiceFields();
|
||||
|
||||
$builder->add(
|
||||
'glue',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'choices' => [
|
||||
'mautic.lead.list.form.glue.and' => 'and',
|
||||
'mautic.lead.list.form.glue.or' => 'or',
|
||||
],
|
||||
'attr' => [
|
||||
'class' => 'label label-warm-gray not-chosen glue-select',
|
||||
'onchange' => 'Mautic.updateFilterPositioning(this)',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$formModifier = function (FormEvent $event) use ($fieldChoices): void {
|
||||
$data = (array) $event->getData();
|
||||
$form = $event->getForm();
|
||||
$fieldAlias = $data['field'] ?? null;
|
||||
$fieldObject = $data['object'] ?? 'behaviors';
|
||||
// Looking for behaviors for BC reasons as some filters were moved from 'lead' to 'behaviors'.
|
||||
$field = $fieldChoices[$fieldObject][$fieldAlias] ?? $fieldChoices['behaviors'][$fieldAlias] ?? null;
|
||||
$operators = $field['operators'] ?? [];
|
||||
$operator = $data['operator'] ?? null;
|
||||
|
||||
if ($operators && !$operator) {
|
||||
$operator = array_key_first($operators);
|
||||
}
|
||||
|
||||
$form->add(
|
||||
'operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'choices' => $operators,
|
||||
'attr' => [
|
||||
'class' => 'form-control not-chosen',
|
||||
'onchange' => 'Mautic.convertLeadFilterInput(this)',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$form->add(
|
||||
'properties',
|
||||
FilterPropertiesType::class,
|
||||
[
|
||||
'label' => false,
|
||||
]
|
||||
);
|
||||
|
||||
if (null === $field) {
|
||||
// The field was probably deleted since the segment was created.
|
||||
// Do not show up the filter based on a deleted field.
|
||||
return;
|
||||
}
|
||||
|
||||
$filterPropertiesType = $form->get('properties');
|
||||
|
||||
$this->setPropertiesFormData($filterPropertiesType, $data);
|
||||
|
||||
if ($fieldAlias && $operator) {
|
||||
$this->formAdjustmentsProvider->adjustForm(
|
||||
$filterPropertiesType,
|
||||
$fieldAlias,
|
||||
$fieldObject,
|
||||
$operator,
|
||||
$field
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, $formModifier);
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, $formModifier);
|
||||
$builder->add('field', HiddenType::class);
|
||||
$builder->add('object', HiddenType::class);
|
||||
$builder->add('type', HiddenType::class);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'label' => false,
|
||||
'error_bubbling' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
$view->vars['fields'] = $this->listModel->getChoiceFields();
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadlist_filter';
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to ensure that the old data[filter] and data[display] will get to the properties form
|
||||
* to keep BC for segments created before the properties form was added and the fitler and display
|
||||
* fields were moved there.
|
||||
*
|
||||
* @param FormInterface<mixed> $filterPropertiesType
|
||||
* @param mixed[] $data
|
||||
*/
|
||||
private function setPropertiesFormData(FormInterface $filterPropertiesType, array $data): void
|
||||
{
|
||||
if (empty($data['properties'])) {
|
||||
$propertiesData = [
|
||||
'filter' => $data['filter'] ?? null,
|
||||
'display' => $data['display'] ?? null,
|
||||
];
|
||||
$filterPropertiesType->setData($propertiesData);
|
||||
} else {
|
||||
$filterPropertiesType->setData($data['properties'] ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\PointBundle\Form\Type\GroupListType;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class FormSubmitActionPointsChangeType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'operator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.submitaction.operator',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'choices' => [
|
||||
'mautic.lead.lead.submitaction.operator_plus' => 'plus',
|
||||
'mautic.lead.lead.submitaction.operator_minus' => 'minus',
|
||||
'mautic.lead.lead.submitaction.operator_times' => 'times',
|
||||
'mautic.lead.lead.submitaction.operator_divide' => 'divide',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$default = (empty($options['data']['points'])) ? 0 : (int) $options['data']['points'];
|
||||
$builder->add(
|
||||
'points',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.submitaction.points',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'scale' => 0,
|
||||
'data' => $default,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('group', GroupListType::class, [
|
||||
'label' => 'mautic.lead.campaign.event.point_group',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.campaign.event.point_group.help',
|
||||
],
|
||||
'required' => false,
|
||||
'by_reference' => false,
|
||||
'return_entity' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_submitaction_pointschange';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\EntityLookupType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class GlobalCategoryType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'required' => false,
|
||||
'model' => 'category.category',
|
||||
'multiple' => true,
|
||||
'ajax_lookup_action' => function (Options $options) {
|
||||
$query = [
|
||||
'for_lookup' => 1,
|
||||
];
|
||||
|
||||
return 'lead:getLookupChoiceList&'.http_build_query($query);
|
||||
},
|
||||
'model_lookup_method' => 'getLookupResults',
|
||||
'lookup_arguments' => function (Options $options) {
|
||||
return [
|
||||
'type' => 'global',
|
||||
'filter' => '$data',
|
||||
'limit' => 10,
|
||||
'start' => 0,
|
||||
'options' => [
|
||||
'is_published' => $options['is_published'],
|
||||
'for_lookup' => 1,
|
||||
],
|
||||
];
|
||||
},
|
||||
'is_published' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return EntityLookupType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
|
||||
class HtmlType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return TextareaType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CategoryBundle\Model\CategoryModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class LeadCategoryType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private CategoryModel $categoryModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choices' => function (Options $options): array {
|
||||
$categories = $this->categoryModel->getLookupResults('email', '', 0);
|
||||
$choices = [];
|
||||
|
||||
foreach ($categories as $cat) {
|
||||
$choices[$cat['title']] = $cat['id'];
|
||||
}
|
||||
|
||||
return $choices;
|
||||
},
|
||||
'global_only' => true,
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadcategory_choices';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Helper\ArrayHelper;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class LeadFieldsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
protected FieldModel $fieldModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choices' => function (Options $options): array {
|
||||
$fieldList = ArrayHelper::flipArray($this->fieldModel->getFieldList());
|
||||
if ($options['with_tags']) {
|
||||
$fieldList['Core']['mautic.lead.field.tags'] = 'tags';
|
||||
}
|
||||
if ($options['with_company_fields']) {
|
||||
$fieldList['Company'] = array_flip($this->fieldModel->getFieldList(false, true, ['isPublished' => true, 'object' => 'company']));
|
||||
}
|
||||
if ($options['with_utm']) {
|
||||
$fieldList['UTM']['mautic.lead.field.utmcampaign'] = 'utm_campaign';
|
||||
$fieldList['UTM']['mautic.lead.field.utmcontent'] = 'utm_content';
|
||||
$fieldList['UTM']['mautic.lead.field.utmmedium'] = 'utm_medium';
|
||||
$fieldList['UTM']['mautic.lead.field.umtsource'] = 'utm_source';
|
||||
$fieldList['UTM']['mautic.lead.field.utmterm'] = 'utm_term';
|
||||
}
|
||||
|
||||
return $fieldList;
|
||||
},
|
||||
'global_only' => false,
|
||||
'required' => false,
|
||||
'with_company_fields' => false,
|
||||
'with_tags' => false,
|
||||
'with_utm' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadfields_choices';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Form\DataTransformer\IdToEntityModelTransformer;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Form\Type\UserListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class LeadImportFieldType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
private EntityManager $entityManager,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$choices = [];
|
||||
foreach ($options['all_fields'] as $optionGroup => $fields) {
|
||||
$choices[$optionGroup] = array_flip($fields);
|
||||
}
|
||||
|
||||
foreach ($options['import_fields'] as $field => $label) {
|
||||
$builder->add(
|
||||
$field,
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $choices,
|
||||
'label' => $label,
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'data' => $this->getDefaultValue($field, $options['import_fields']),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$transformer = new IdToEntityModelTransformer($this->entityManager, User::class);
|
||||
|
||||
$builder->add(
|
||||
$builder->create(
|
||||
'owner',
|
||||
UserListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.field.owner',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'multiple' => false,
|
||||
]
|
||||
)
|
||||
->addModelTransformer($transformer)
|
||||
);
|
||||
|
||||
if ('lead' === $options['object']) {
|
||||
$builder->add(
|
||||
$builder->create(
|
||||
'list',
|
||||
LeadListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.field.list',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'multiple' => false,
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'tags',
|
||||
TagType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.tags',
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-placeholder' => $this->translator->trans('mautic.lead.tags.select_or_create'),
|
||||
'data-no-results-text' => $this->translator->trans('mautic.lead.tags.enter_to_create'),
|
||||
'data-allow-add' => 'true',
|
||||
'onchange' => 'Mautic.createLeadTag(this)',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'skip_if_exists',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.import.skip_if_exists',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
'data' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$buttons = ['cancel_icon' => 'ri-close-line'];
|
||||
|
||||
if (empty($options['line_count_limit'])) {
|
||||
$buttons = array_merge(
|
||||
$buttons,
|
||||
[
|
||||
'apply_text' => 'mautic.lead.import.in.background',
|
||||
'apply_class' => 'btn btn-secondary',
|
||||
'apply_icon' => 'ri-history-line',
|
||||
'save_text' => 'mautic.lead.import.start',
|
||||
'save_class' => 'btn btn-secondary',
|
||||
'save_icon' => 'ri-import-line',
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$buttons = array_merge(
|
||||
$buttons,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.lead.import',
|
||||
'save_class' => 'btn btn-primary',
|
||||
'save_icon' => 'ri-import-line',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class, $buttons);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['all_fields', 'import_fields', 'object']);
|
||||
$resolver->setDefaults([
|
||||
'line_count_limit' => 0,
|
||||
'validation_groups' => [
|
||||
User::class,
|
||||
'determineValidationGroups',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_field_import';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultValue($fieldName, array $importFields)
|
||||
{
|
||||
return $importFields[$fieldName] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Validator\Constraints\FileEncoding as EncodingValidation;
|
||||
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\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\File;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class LeadImportType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'file',
|
||||
FileType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.import.file',
|
||||
'attr' => [
|
||||
'accept' => '.csv',
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'constraints' => [
|
||||
new File(
|
||||
[
|
||||
'mimeTypes' => ['text/*', 'application/octet-stream', 'application/csv'],
|
||||
'mimeTypesMessage' => 'mautic.core.invalid_file_type',
|
||||
]
|
||||
),
|
||||
new EncodingValidation(
|
||||
[
|
||||
'encodingFormat' => ['UTF-8'],
|
||||
'encodingFormatMessage' => 'mautic.core.invalid_file_encoding',
|
||||
]
|
||||
),
|
||||
new NotBlank(
|
||||
['message' => 'mautic.import.file.required']
|
||||
),
|
||||
],
|
||||
'error_bubbling' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$constraints = [
|
||||
new NotBlank(
|
||||
['message' => 'mautic.core.value.required']
|
||||
),
|
||||
];
|
||||
|
||||
$default = (empty($options['data']['delimiter'])) ? ',' : htmlspecialchars($options['data']['delimiter']);
|
||||
$builder->add(
|
||||
'delimiter',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.import.delimiter',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.import.delimiter.help',
|
||||
],
|
||||
'data' => $default,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
|
||||
$default = (empty($options['data']['enclosure'])) ? '"' : htmlspecialchars($options['data']['enclosure']);
|
||||
$builder->add(
|
||||
'enclosure',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.import.enclosure',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.import.enclosure.help',
|
||||
],
|
||||
'data' => $default,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
|
||||
$default = (empty($options['data']['escape'])) ? '\\' : $options['data']['escape'];
|
||||
$builder->add(
|
||||
'escape',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.import.escape',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.import.escape.help',
|
||||
],
|
||||
'data' => $default,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
|
||||
$default = (empty($options['data']['batchlimit'])) ? 100 : (int) $options['data']['batchlimit'];
|
||||
$builder->add(
|
||||
'batchlimit',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.import.batchlimit',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.import.batchlimit_tooltip',
|
||||
],
|
||||
'data' => $default,
|
||||
'constraints' => $constraints,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'start',
|
||||
SubmitType::class,
|
||||
[
|
||||
'attr' => [
|
||||
'class' => 'btn btn-tertiary',
|
||||
'icon' => 'ri-import-line',
|
||||
'onclick' => "mQuery(this).prop('disabled', true); mQuery('form[name=\'lead_import\']').submit();",
|
||||
],
|
||||
'label' => 'mautic.lead.import.upload',
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class LeadListType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private ListModel $segmentModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choices' => function (Options $options): array {
|
||||
$lists = (empty($options['global_only'])) ? $this->segmentModel->getUserLists() : $this->segmentModel->getGlobalLists();
|
||||
$lists = (empty($options['preference_center_only'])) ? $lists : $this->segmentModel->getPreferenceCenterLists();
|
||||
|
||||
$choices = [];
|
||||
foreach ($lists as $l) {
|
||||
if (empty($options['preference_center_only'])) {
|
||||
$choices[$l['name'].' ('.$l['id'].')'] = $l['id'];
|
||||
} else {
|
||||
$choices[empty($l['publicName']) ? $l['name'].' ('.$l['id'].')' : $l['publicName'].' ('.$l['id'].')'] = $l['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $choices;
|
||||
},
|
||||
'global_only' => false,
|
||||
'preference_center_only' => false,
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadlist_choices';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Form\DataTransformer\IdToEntityModelTransformer;
|
||||
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;
|
||||
use Mautic\CoreBundle\Form\EventListener\FormExitSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Model\CompanyModel;
|
||||
use Mautic\StageBundle\Entity\Stage;
|
||||
use Mautic\StageBundle\Form\Type\StageListType;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Form\Type\UserListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\File;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<Lead>
|
||||
*/
|
||||
class LeadType extends AbstractType
|
||||
{
|
||||
use EntityFieldsBuildFormTrait;
|
||||
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
private CompanyModel $companyModel,
|
||||
private EntityManager $entityManager,
|
||||
private CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventSubscriber(new FormExitSubscriber('lead.lead', $options));
|
||||
|
||||
if (!$options['isShortForm']) {
|
||||
$imageChoices = [
|
||||
'Gravatar' => 'gravatar',
|
||||
'mautic.lead.lead.field.custom_avatar' => 'custom',
|
||||
];
|
||||
|
||||
$cache = $options['data']->getSocialCache() ?? [];
|
||||
if (count($cache)) {
|
||||
foreach ($cache as $key => $data) {
|
||||
$imageChoices[$key] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'preferred_profile_image',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $imageChoices,
|
||||
'label' => 'mautic.lead.lead.field.preferred_profile',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => true,
|
||||
'multiple' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'custom_avatar',
|
||||
FileType::class,
|
||||
[
|
||||
'label' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'mapped' => false,
|
||||
'constraints' => [
|
||||
new File(
|
||||
[
|
||||
'mimeTypes' => [
|
||||
'image/gif',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
],
|
||||
'mimeTypesMessage' => 'mautic.lead.avatar.types_invalid',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$cleaningRules = $this->getFormFields($builder, $options);
|
||||
$cleaningRules['email'] = 'email';
|
||||
|
||||
$builder->add(
|
||||
'tags',
|
||||
TagType::class,
|
||||
[
|
||||
'by_reference' => false,
|
||||
'attr' => [
|
||||
'id' => 'lead_tags',
|
||||
'data-placeholder' => $this->translator->trans('mautic.lead.tags.select_or_create'),
|
||||
'data-no-results-text' => $this->translator->trans('mautic.lead.tags.enter_to_create'),
|
||||
'data-allow-add' => 'true',
|
||||
'onchange' => 'Mautic.createLeadTag(this)',
|
||||
'autocomplete' => 'off',
|
||||
'multiple' => 'multiple',
|
||||
'aria-label' => $this->translator->trans('mautic.lead.tags.aria.label'),
|
||||
'aria-describedby' => 'lead_tags_help',
|
||||
'aria-expanded' => 'false',
|
||||
'role' => 'combobox',
|
||||
'aria-multiselectable' => 'true',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$allowMultipleCompanies = $this->coreParametersHelper->get('contact_allow_multiple_companies');
|
||||
$companyIds = $this->companyModel->getCompanyLeadRepository()->getCompanyIdsByLeadId((string) $options['data']->getId());
|
||||
|
||||
$builder->add(
|
||||
'companies',
|
||||
CompanyListType::class,
|
||||
[
|
||||
'label' => 'mautic.company.selectcompany',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => $allowMultipleCompanies,
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
'data' => !$allowMultipleCompanies ? ($companyIds[0] ?? null) : array_combine($companyIds, $companyIds),
|
||||
]
|
||||
);
|
||||
|
||||
$transformer = new IdToEntityModelTransformer($this->entityManager, User::class);
|
||||
|
||||
$builder->add(
|
||||
$builder->create(
|
||||
'owner',
|
||||
UserListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.field.owner',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'multiple' => false,
|
||||
]
|
||||
)
|
||||
->addModelTransformer($transformer)
|
||||
);
|
||||
|
||||
$transformer = new IdToEntityModelTransformer($this->entityManager, Stage::class);
|
||||
|
||||
$builder->add(
|
||||
$builder->create(
|
||||
'stage',
|
||||
StageListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.field.stage',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
'multiple' => false,
|
||||
]
|
||||
)
|
||||
->addModelTransformer($transformer)
|
||||
);
|
||||
|
||||
if (!$options['isShortForm']) {
|
||||
$builder->add('buttons', FormButtonsType::class);
|
||||
} else {
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber($cleaningRules));
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'data_class' => Lead::class,
|
||||
'isShortForm' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$resolver->setRequired(['fields', 'isShortForm']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ListActionType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'addToLists',
|
||||
LeadListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.events.addtolists',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'removeFromLists',
|
||||
LeadListType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.events.removefromlists',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadlist_action';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
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\YesNoButtonGroupType;
|
||||
use Mautic\CoreBundle\Form\Validator\Constraints\CircularDependency;
|
||||
use Mautic\LeadBundle\Entity\LeadList;
|
||||
use Mautic\LeadBundle\Form\DataTransformer\FieldFilterTransformer;
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Mautic\LeadBundle\Validator\Constraints\SegmentDate;
|
||||
use Mautic\ProjectBundle\Form\Type\ProjectType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<LeadList>
|
||||
*/
|
||||
class ListType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
private ListModel $listModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber(['description' => 'html', 'name' => 'string', 'publicName' => 'string', 'filter' => 'raw']));
|
||||
$builder->addEventSubscriber(new FormExitSubscriber('lead.list', $options));
|
||||
|
||||
$builder->add(
|
||||
'name',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.name',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'publicName',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.form.publicname',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.list.form.publicname.tooltip',
|
||||
'placeholder' => 'mautic.core.autogenerated',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'alias',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.alias',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'length' => 25,
|
||||
'tooltip' => 'mautic.lead.list.help.alias',
|
||||
'placeholder' => 'mautic.core.autogenerated',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'description',
|
||||
TextareaType::class,
|
||||
[
|
||||
'label' => 'mautic.core.description',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control editor'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'category',
|
||||
CategoryListType::class,
|
||||
[
|
||||
'bundle' => 'segment',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isGlobal',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.form.isglobal',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.lead.list.form.isglobal.tooltip',
|
||||
],
|
||||
'no_label' => 'mautic.lead.list.form.isglobal.no',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'isPreferenceCenter',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.form.isPreferenceCenter',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.lead.list.form.isPreferenceCenter.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('projects', ProjectType::class);
|
||||
$builder->add('isPublished', YesNoButtonGroupType::class);
|
||||
|
||||
$filterModalTransformer = new FieldFilterTransformer($this->translator, ['object' => 'lead']);
|
||||
$builder->add(
|
||||
$builder->create(
|
||||
'filters',
|
||||
CollectionType::class,
|
||||
[
|
||||
'entry_type' => FilterType::class,
|
||||
'error_bubbling' => false,
|
||||
'mapped' => true,
|
||||
'allow_add' => true,
|
||||
'allow_delete' => true,
|
||||
'label' => false,
|
||||
'constraints' => [
|
||||
new CircularDependency([
|
||||
'message' => 'mautic.core.segment.circular_dependency_exists',
|
||||
]),
|
||||
new SegmentDate([
|
||||
'message' => 'mautic.lead.segment.date_invalid',
|
||||
]),
|
||||
],
|
||||
]
|
||||
)->addModelTransformer($filterModalTransformer)
|
||||
);
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'data_class' => LeadList::class,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
$view->vars['fields'] = $this->listModel->getChoiceFields();
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadlist';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\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\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class MergeType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'lead_to_merge',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $options['leads'],
|
||||
'label' => 'mautic.lead.merge.select',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => false,
|
||||
'placeholder' => '',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.merge.select.modal.tooltip',
|
||||
],
|
||||
'constraints' => [
|
||||
new NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.lead.merge',
|
||||
'save_icon' => 'ri-user-6-line',
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(['leads']);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_merge';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class ModifyLeadTagsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'add_tags',
|
||||
TagType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.tags.add',
|
||||
'attr' => [
|
||||
'data-placeholder' => $this->translator->trans('mautic.lead.tags.select_or_create'),
|
||||
'data-no-results-text' => $this->translator->trans('mautic.lead.tags.enter_to_create'),
|
||||
'data-allow-add' => 'true',
|
||||
'onchange' => 'Mautic.createLeadTag(this)',
|
||||
],
|
||||
'data' => $options['data']['add_tags'] ?? null,
|
||||
'add_transformer' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'remove_tags',
|
||||
TagType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.tags.remove',
|
||||
'attr' => [
|
||||
'data-placeholder' => $this->translator->trans('mautic.lead.tags.select_or_create'),
|
||||
'data-no-results-text' => $this->translator->trans('mautic.lead.tags.enter_to_create'),
|
||||
'data-allow-add' => 'true',
|
||||
'onchange' => 'Mautic.createLeadTag(this)',
|
||||
],
|
||||
'data' => $options['data']['remove_tags'] ?? null,
|
||||
'add_transformer' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;
|
||||
use Mautic\CoreBundle\Form\EventListener\FormExitSubscriber;
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\LeadBundle\Entity\LeadNote;
|
||||
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\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<LeadNote>
|
||||
*/
|
||||
class NoteType extends AbstractType
|
||||
{
|
||||
private DateTimeHelper $dateHelper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->dateHelper = new DateTimeHelper();
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventSubscriber(new CleanFormSubscriber(['text' => 'html']));
|
||||
$builder->addEventSubscriber(new FormExitSubscriber('lead.note', $options));
|
||||
|
||||
$builder->add(
|
||||
'text',
|
||||
TextareaType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.note.form.text',
|
||||
'label_attr' => ['class' => 'control-label sr-only'],
|
||||
'attr' => ['class' => 'mousetrap form-control editor', 'rows' => 10, 'autofocus' => 'autofocus'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'type',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.note.form.type',
|
||||
'choices' => [
|
||||
'mautic.lead.note.type.general' => 'general',
|
||||
'mautic.lead.note.type.email' => 'email',
|
||||
'mautic.lead.note.type.call' => 'call',
|
||||
'mautic.lead.note.type.meeting' => 'meeting',
|
||||
],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$dt = $options['data']->getDatetime();
|
||||
$data = (null == $dt) ? $this->dateHelper->getDateTime() : $dt;
|
||||
|
||||
$builder->add(
|
||||
'dateTime',
|
||||
DateTimeType::class,
|
||||
[
|
||||
'label' => 'mautic.core.date.added',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'widget' => 'single_text',
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'data-toggle' => 'datetime',
|
||||
'preaddon' => 'ri-calendar-line',
|
||||
],
|
||||
'format' => 'yyyy-MM-dd HH:mm',
|
||||
'html5' => false,
|
||||
'data' => $data,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('buttons', FormButtonsType::class, [
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
]);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => LeadNote::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadnote';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\ButtonGroupType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class NullableYesNoButtonGroupType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return ButtonGroupType::class;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'choices' => fn (Options $options): array => [
|
||||
$options['no_label'] => $options['no_value'],
|
||||
$options['yes_label'] => $options['yes_value'],
|
||||
],
|
||||
'choice_value' => function ($choiceKey) {
|
||||
if (null === $choiceKey || '' === $choiceKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (is_string($choiceKey) && !is_numeric($choiceKey)) ? $choiceKey : (int) $choiceKey;
|
||||
},
|
||||
'expanded' => true,
|
||||
'multiple' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'label' => 'mautic.core.form.active',
|
||||
'placeholder' => true,
|
||||
'required' => false,
|
||||
'no_label' => 'mautic.core.form.no',
|
||||
'no_value' => 0,
|
||||
'yes_label' => 'mautic.core.form.yes',
|
||||
'yes_value' => 1,
|
||||
'empty_data' => null,
|
||||
'data' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\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;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class OwnerType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'addowner',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.add_to',
|
||||
'multiple' => false,
|
||||
'choices' => $options['items'],
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('ids', HiddenType::class);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(
|
||||
[
|
||||
'items',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_batch_owner';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\PointBundle\Form\Type\GroupListType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotEqualTo;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class PointActionType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'points',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.lead.event.points',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'scale' => 0,
|
||||
'data' => $options['data']['points'] ?? 0,
|
||||
'constraints' => [
|
||||
new NotEqualTo(
|
||||
[
|
||||
'value' => '0',
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('group', GroupListType::class, [
|
||||
'label' => 'mautic.lead.campaign.event.point_group',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.campaign.event.point_group.help',
|
||||
],
|
||||
'required' => false,
|
||||
'by_reference' => false,
|
||||
'return_entity' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'leadpoints_action';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class PreferenceChannelsType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private LeadModel $leadModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$model = $this->leadModel;
|
||||
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'choices' => fn (Options $options) => $model->getPreferenceChannels(),
|
||||
'placeholder' => '',
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class SegmentConfigType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @param FormBuilderInterface<FormBuilderInterface> $builder
|
||||
* @param mixed[] $options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'segment_rebuild_time_warning',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.form.config.segment_rebuild_time_warning',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.list.form.config.segment_rebuild_time_warning.tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'segment_build_time_warning',
|
||||
NumberType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.list.form.config.segment_build_time_warning',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.lead.list.form.config.segment_build_time_warning.tooltip',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\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;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class StageType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'addstage',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'mautic.lead.batch.add_to',
|
||||
'multiple' => false,
|
||||
'choices' => $options['items'],
|
||||
'required' => false,
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('ids', HiddenType::class);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.save',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setRequired(
|
||||
[
|
||||
'items',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_batch_stage';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class TagEntityType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('tag', TextType::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Mautic\LeadBundle\Entity\Tag;
|
||||
use Mautic\LeadBundle\Form\DataTransformer\TagEntityModelTransformer;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<Tag>
|
||||
*/
|
||||
class TagType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $em,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
if ($options['add_transformer']) {
|
||||
$transformer = new TagEntityModelTransformer(
|
||||
$this->em,
|
||||
Tag::class,
|
||||
$options['multiple']
|
||||
);
|
||||
|
||||
$builder->addModelTransformer($transformer);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'label' => 'mautic.lead.tags',
|
||||
'class' => Tag::class,
|
||||
'query_builder' => fn (EntityRepository $er) => $er->createQueryBuilder('t')->orderBy('t.tag', Order::Ascending->value),
|
||||
'choice_label' => 'tag',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'disabled' => false,
|
||||
'add_transformer' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'lead_tag';
|
||||
}
|
||||
|
||||
public function getParent(): ?string
|
||||
{
|
||||
return EntityType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Cache\ResultCacheOptions;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class UpdateCompanyActionType extends AbstractType
|
||||
{
|
||||
use EntityFieldsBuildFormTrait;
|
||||
|
||||
public function __construct(
|
||||
protected FieldModel $fieldModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$leadFields = $this->fieldModel->getEntities(
|
||||
[
|
||||
'force' => [
|
||||
[
|
||||
'column' => 'f.isPublished',
|
||||
'expr' => 'eq',
|
||||
'value' => true,
|
||||
],
|
||||
],
|
||||
'hydration_mode' => 'HYDRATE_ARRAY',
|
||||
'result_cache' => new ResultCacheOptions(LeadField::CACHE_NAMESPACE),
|
||||
]
|
||||
);
|
||||
|
||||
$options['fields'] = $leadFields;
|
||||
$options['ignore_required_constraints'] = true;
|
||||
$options['use_nullable_yes_no_type'] = true;
|
||||
|
||||
$this->getFormFields($builder, $options, 'company');
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'updatecompany_action';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Cache\ResultCacheOptions;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class UpdateLeadActionType extends AbstractType
|
||||
{
|
||||
use EntityFieldsBuildFormTrait;
|
||||
|
||||
public function __construct(
|
||||
private FieldModel $fieldModel,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$leadFields = $this->fieldModel->getEntities(
|
||||
[
|
||||
'force' => [
|
||||
[
|
||||
'column' => 'f.isPublished',
|
||||
'expr' => 'eq',
|
||||
'value' => true,
|
||||
],
|
||||
],
|
||||
'hydration_mode' => 'HYDRATE_ARRAY',
|
||||
'result_cache' => new ResultCacheOptions(LeadField::CACHE_NAMESPACE),
|
||||
]
|
||||
);
|
||||
|
||||
$options['fields'] = $leadFields;
|
||||
$options['ignore_required_constraints'] = true;
|
||||
$options['ignore_date_type'] = true;
|
||||
$options['use_nullable_yes_no_type'] = true;
|
||||
|
||||
$this->getFormFields($builder, $options);
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'updatelead_action';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
final class DbRegex extends Constraint
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class DbRegexValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(private Connection $connection)
|
||||
{
|
||||
}
|
||||
|
||||
public function validate(mixed $regex, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof DbRegex) {
|
||||
throw new UnexpectedTypeException($constraint, DbRegex::class);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->connection->executeQuery('SELECT 1 REGEXP ? AS is_valid', [$regex]);
|
||||
} catch (Exception $e) {
|
||||
$this->context->buildViolation(
|
||||
$this->stripUglyPartOfTheErrorMessage($e->getPrevious()->getMessage())
|
||||
)->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
private function stripUglyPartOfTheErrorMessage(string $message): string
|
||||
{
|
||||
return preg_replace('/SQLSTATE\[\d+\]: [\w ]+: \d+ /', '', $message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute]
|
||||
class EmailAddress extends Constraint
|
||||
{
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return static::class.'Validator';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Mautic\EmailBundle\Exception\InvalidEmailException;
|
||||
use Mautic\EmailBundle\Helper\EmailValidator;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
class EmailAddressValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private EmailValidator $emailValidator,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function validate($value, Constraint $constraint): void
|
||||
{
|
||||
if (!empty($value)) {
|
||||
try {
|
||||
$this->emailValidator->validate($value);
|
||||
} catch (InvalidEmailException $invalidEmailException) {
|
||||
$this->context->addViolation(
|
||||
$invalidEmailException->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class FieldAliasKeyword extends Constraint
|
||||
{
|
||||
public $message = 'mautic.lead.field.keyword.invalid';
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return FieldAliasKeywordValidator::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Helper\FieldAliasHelper;
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Mautic\LeadBundle\Services\ContactSegmentFilterDictionary;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Throws an exception if the field alias is equal some segment filter keyword.
|
||||
* It would cause odd behavior with segment filters otherwise.
|
||||
*/
|
||||
class FieldAliasKeywordValidator extends ConstraintValidator
|
||||
{
|
||||
public const RESTRICTED_ALIASES = [
|
||||
'contact_id',
|
||||
'company_id',
|
||||
'notes',
|
||||
'owner',
|
||||
'id',
|
||||
'ip',
|
||||
'tags',
|
||||
'dateAdded',
|
||||
'dateModified',
|
||||
'lastActive',
|
||||
'createdByUser',
|
||||
'modifiedByUser',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private ListModel $listModel,
|
||||
private FieldAliasHelper $aliasHelper,
|
||||
private EntityManager $em,
|
||||
private TranslatorInterface $translator,
|
||||
private ContactSegmentFilterDictionary $contactSegmentFilterDictionary,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LeadField $field
|
||||
*/
|
||||
public function validate($field, Constraint $constraint): void
|
||||
{
|
||||
$oldValue = $this->em->getUnitOfWork()->getOriginalEntityData($field);
|
||||
$this->aliasHelper->makeAliasUnique($field);
|
||||
|
||||
// If empty it's a new object else it's an edit
|
||||
if (empty($oldValue) || (!empty($oldValue) && is_array($oldValue) && $oldValue['alias'] != $field->getAlias())) {
|
||||
if (in_array($field->getAlias(), self::RESTRICTED_ALIASES)) {
|
||||
$this->context->addViolation(
|
||||
$this->translator->trans(
|
||||
'mautic.lead.field.keyword.restricted',
|
||||
['%alias%' => $field->getAlias()],
|
||||
'validators'
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
$choices = array_merge($this->listModel->getChoiceFields()[$field->getObject()] ?? [], $this->contactSegmentFilterDictionary->getFilters());
|
||||
|
||||
if (isset($choices[$field->getAlias()])) {
|
||||
$this->context->addViolation($constraint->message, ['%keyword%' => $field->getAlias()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute]
|
||||
class LeadListAccess extends Constraint
|
||||
{
|
||||
public string $message = 'mautic.lead.lists.failed';
|
||||
public bool $allowEmpty = false;
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return 'leadlist_access';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class LeadListAccessValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private ListModel $segmentModel,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function validate($value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof LeadListAccess) {
|
||||
throw new UnexpectedTypeException($constraint, LeadListAccess::class);
|
||||
}
|
||||
|
||||
if (count($value)) {
|
||||
$lists = $this->segmentModel->getUserLists();
|
||||
foreach ($value as $l) {
|
||||
if (!isset($lists[$l->getId()])) {
|
||||
$this->context->addViolation(
|
||||
$constraint->message,
|
||||
['%string%' => $l->getName()]
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif (!$constraint->allowEmpty) {
|
||||
$this->context->addViolation($constraint->message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class SegmentInUse extends Constraint
|
||||
{
|
||||
public $message = 'mautic.lead_list.is_in_use';
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return 'segment_in_use';
|
||||
}
|
||||
|
||||
public function getTargets(): string
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Mautic\LeadBundle\Entity\LeadList;
|
||||
use Mautic\LeadBundle\Model\ListModel;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class SegmentInUseValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private ListModel $listModel,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LeadList $leadList
|
||||
*/
|
||||
public function validate($leadList, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof SegmentInUse) {
|
||||
throw new UnexpectedTypeException($constraint, SegmentInUse::class);
|
||||
}
|
||||
|
||||
if (!$leadList->getId() || $leadList->getIsPublished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lists = $this->listModel->getSegmentsWithDependenciesOnSegment($leadList->getId(), 'name');
|
||||
|
||||
if (count($lists)) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setCode((string) Response::HTTP_UNPROCESSABLE_ENTITY)
|
||||
->setParameter('%segments%', implode(',', $lists))
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class UniqueCustomField extends Constraint
|
||||
{
|
||||
public string $message = 'mautic.lead.field.unique.is_used';
|
||||
|
||||
public string $object;
|
||||
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Field\FieldsWithUniqueIdentifier;
|
||||
use Mautic\LeadBundle\Model\CompanyModel;
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
class UniqueCustomFieldValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private LeadModel $leadModel,
|
||||
private CompanyModel $companyModel,
|
||||
private FieldsWithUniqueIdentifier $fieldsWithUniqueIdentifier,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Lead|Company|mixed $object
|
||||
*/
|
||||
public function validate($object, Constraint $constraint): void
|
||||
{
|
||||
\assert($constraint instanceof UniqueCustomField);
|
||||
\assert($object instanceof Lead || $object instanceof Company);
|
||||
|
||||
$form = $this->context->getRoot();
|
||||
|
||||
// When using API Platform, the root is not a Form instance
|
||||
if (!$form instanceof Form) {
|
||||
return;
|
||||
}
|
||||
|
||||
$publishedUniqueFields = $this->fieldsWithUniqueIdentifier->getFieldsWithUniqueIdentifier([
|
||||
'isPublished' => true,
|
||||
'isUniqueIdentifer' => true,
|
||||
'object' => $constraint->object,
|
||||
]);
|
||||
|
||||
$publishedUniqueFields = array_keys($publishedUniqueFields);
|
||||
|
||||
$uniqueFieldsData = [];
|
||||
foreach ($publishedUniqueFields as $publishedUniqueField) {
|
||||
if (!$form->has($publishedUniqueField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $form->get($publishedUniqueField)->getData();
|
||||
if (null === $data || '' === $data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$uniqueFieldsData[$publishedUniqueField] = $data;
|
||||
}
|
||||
|
||||
$validatedFields = [];
|
||||
if ($object instanceof Lead) {
|
||||
$validatedFields = $this->getLeadFieldsValid($object, $uniqueFieldsData);
|
||||
}
|
||||
|
||||
if ($object instanceof Company) {
|
||||
$validatedFields = $this->getCompanyFieldsValid($object, $uniqueFieldsData);
|
||||
}
|
||||
|
||||
foreach ($validatedFields as $fieldName => $isValid) {
|
||||
if ($isValid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setCode((string) Response::HTTP_UNPROCESSABLE_ENTITY)
|
||||
->atPath($fieldName)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $fieldsData
|
||||
*
|
||||
* @return array<bool>
|
||||
*/
|
||||
private function getLeadFieldsValid(Lead $lead, array $fieldsData): array
|
||||
{
|
||||
$leadRepository = $this->leadModel->getRepository();
|
||||
if ('orWhere' === $leadRepository->getUniqueIdentifiersWherePart()) {
|
||||
$fieldsValidation = [];
|
||||
foreach ($fieldsData as $field => $data) {
|
||||
$leads = $leadRepository->getLeadIdsByUniqueFields([$field => $data]);
|
||||
|
||||
$fieldsValidation[] = $this->isValid($leads, [$field], (int) $lead->getId());
|
||||
}
|
||||
|
||||
return array_merge(...$fieldsValidation);
|
||||
}
|
||||
|
||||
// Can't use getEntities, because it refreshes some field data, that can be used in the form
|
||||
$leads = $leadRepository->getLeadIdsByUniqueFields($fieldsData);
|
||||
|
||||
return $this->isValid($leads, array_keys($fieldsData), (int) $lead->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $fieldsData
|
||||
*
|
||||
* @return array<bool>
|
||||
*/
|
||||
private function getCompanyFieldsValid(Company $company, array $fieldsData): array
|
||||
{
|
||||
$companyRepository = $this->companyModel->getRepository();
|
||||
if ('orWhere' === $companyRepository->getUniqueIdentifiersWherePart()) {
|
||||
$fieldsValidation = [];
|
||||
foreach ($fieldsData as $field => $data) {
|
||||
$companies = $companyRepository->getCompanyIdsByUniqueFields([$field => $data]);
|
||||
|
||||
$fieldsValidation[] = $this->isValid($companies, [$field], (int) $company->getId());
|
||||
}
|
||||
|
||||
return array_merge(...$fieldsValidation);
|
||||
}
|
||||
|
||||
// Can't use getEntities, because it refreshes some field data, that can be used in the form
|
||||
$companies = $companyRepository->getCompanyIdsByUniqueFields($fieldsData);
|
||||
|
||||
return $this->isValid($companies, array_keys($fieldsData), (int) $company->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<mixed>> $objects
|
||||
* @param array<string> $fields
|
||||
*
|
||||
* @return array<bool>
|
||||
*/
|
||||
private function isValid(array $objects, array $fields, int $objectId): array
|
||||
{
|
||||
$objectsCount = count($objects);
|
||||
if (0 === $objectsCount) {
|
||||
return array_fill_keys($fields, true);
|
||||
}
|
||||
|
||||
if ($objectsCount > 1) {
|
||||
return array_fill_keys($fields, false);
|
||||
}
|
||||
|
||||
if ((int) $objects[0]['id'] === $objectId) {
|
||||
return array_fill_keys($fields, true);
|
||||
}
|
||||
|
||||
return array_fill_keys($fields, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute]
|
||||
class UniqueUserAlias extends Constraint
|
||||
{
|
||||
public $message = 'This alias is already in use.';
|
||||
|
||||
public $field = '';
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return 'uniqueleadlist';
|
||||
}
|
||||
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
public function getRequiredOptions(): array
|
||||
{
|
||||
return ['field'];
|
||||
}
|
||||
|
||||
public function getDefaultOption(): ?string
|
||||
{
|
||||
return 'field';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\LeadBundle\Form\Validator\Constraints;
|
||||
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\LeadBundle\Entity\LeadListRepository;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||
|
||||
class UniqueUserAliasValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var LeadListRepository
|
||||
*/
|
||||
public $segmentRepository;
|
||||
|
||||
/**
|
||||
* @var UserHelper
|
||||
*/
|
||||
public $userHelper;
|
||||
|
||||
public function __construct(LeadListRepository $segmentRepository, UserHelper $userHelper)
|
||||
{
|
||||
$this->segmentRepository = $segmentRepository;
|
||||
$this->userHelper = $userHelper;
|
||||
}
|
||||
|
||||
public function validate(mixed $list, Constraint $constraint): void
|
||||
{
|
||||
$field = $constraint->field;
|
||||
|
||||
if (empty($field)) {
|
||||
throw new ConstraintDefinitionException('A field has to be specified.');
|
||||
}
|
||||
|
||||
if ($list->getAlias()) {
|
||||
$lists = $this->segmentRepository->getLists(
|
||||
$this->userHelper->getUser(),
|
||||
$list->getAlias(),
|
||||
$list->getId()
|
||||
);
|
||||
|
||||
if (count($lists)) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->atPath($field)
|
||||
->setParameter('%alias%', $list->getAlias())
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user