Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
<?php
namespace Mautic\LeadBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraints\Length as SymfonyLength;
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)]
class Length extends SymfonyLength
{
public function validatedBy(): string
{
return static::class.'Validator';
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Mautic\LeadBundle\Validator\Constraints;
use Mautic\LeadBundle\Helper\FormFieldHelper;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\LengthValidator as SymfonyLengthValidator;
class LengthValidator extends SymfonyLengthValidator
{
public function validate(mixed $value, Constraint $constraint): void
{
if (is_array($value)) {
$value = FormFieldHelper::formatList(FormFieldHelper::FORMAT_BAR, $value);
}
parent::validate($value, $constraint);
}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class SegmentDate extends Constraint
{
public string $message;
public function validatedBy(): string
{
return SegmentDateValidator::class;
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator\Constraints;
use Mautic\LeadBundle\Segment\ContactSegmentFilterFactory;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Contracts\Translation\TranslatorInterface;
final class SegmentDateValidator extends ConstraintValidator
{
public function __construct(
private ContactSegmentFilterFactory $contactSegmentFilterFactory,
private TranslatorInterface $translator,
) {
}
/**
* @param array<mixed> $filters
*/
public function validate($filters, Constraint $constraint): void
{
foreach ($filters as $filter) {
if (isset($filter['type']) && in_array($filter['type'], ['date', 'datetime'])) {
$segmentFilter = $this->contactSegmentFilterFactory->factorSegmentFilter($filter);
$parameterValue = $segmentFilter->getParameterValue();
if (is_array($parameterValue)) {
continue;
}
if (in_array($filter['operator'] ?? '', ['regexp', '!regexp', 'like', '!like', 'startsWith', 'endsWith', 'contains'])) {
continue;
}
if (null === $parameterValue) {
continue;
}
if (str_contains($parameterValue, '%')) {
return;
}
$formats = ['Y-m-d', 'Y-m-d H:i', 'Y-m-d H:i:s'];
foreach ($formats as $fmt) {
$dateTime = \DateTime::createFromFormat($fmt, $parameterValue);
if (false !== $dateTime) {
break;
}
}
if (false === $dateTime) {
$this->context->addViolation($this->translator->trans('mautic.lead.segment.date_invalid', ['%value%' => $parameterValue], 'validators'));
return;
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class SegmentUsedInCampaigns extends Constraint
{
public function getTargets(): string|array
{
return static::CLASS_CONSTRAINT;
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator\Constraints;
use Mautic\CoreBundle\Exception\RecordNotUnpublishedException;
use Mautic\LeadBundle\Entity\LeadList;
use Mautic\LeadBundle\Validator\SegmentUsedInCampaignsValidator as InternalValidator;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class SegmentUsedInCampaignsValidator extends ConstraintValidator
{
public function __construct(private InternalValidator $internalValidator)
{
}
public function validate(mixed $segment, Constraint $constraint): void
{
try {
/** @var LeadList $segment */
if ($segment->getIsPublished()) {
return;
}
$this->internalValidator->validate($segment);
} catch (RecordNotUnpublishedException $exception) {
$this->context->buildViolation($exception->getMessage())
->atPath('isPublished')
->setCode((string) Response::HTTP_UNPROCESSABLE_ENTITY)
->addViolation();
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator;
use Mautic\CoreBundle\Exception\InvalidValueException;
use Mautic\CoreBundle\Exception\RecordNotFoundException;
use Mautic\CoreBundle\Exception\RecordNotPublishedException;
use Mautic\LeadBundle\Entity\LeadField;
use Mautic\LeadBundle\Model\FieldModel;
use Symfony\Contracts\Translation\TranslatorInterface;
class CustomFieldValidator
{
public function __construct(
private FieldModel $fieldModel,
private TranslatorInterface $translator,
) {
}
/**
* @throws RecordNotFoundException
* @throws RecordNotPublishedException
* @throws InvalidValueException
*/
public function validateFieldType(string $alias, string $fieldType): void
{
$field = $this->getPublishedFieldByAlias($alias);
if ($field->getType() !== $fieldType) {
throw new InvalidValueException($this->translator->trans('mautic.lead.contact.wrong.field.type', ['%alias%' => $alias, '%fieldType%' => $field->getType(), '%expectedType%' => $fieldType], 'validators'));
}
}
/**
* @throws RecordNotFoundException
* @throws RecordNotPublishedException
*/
private function getPublishedFieldByAlias(string $alias): LeadField
{
$field = $this->getFieldByAlias($alias);
if (!$field->getIsPublished()) {
throw new RecordNotPublishedException($this->translator->trans('mautic.lead.contact.field.not.published', ['%alias%' => $alias], 'validators'));
}
return $field;
}
/**
* @throws RecordNotFoundException
*/
private function getFieldByAlias(string $alias): LeadField
{
$field = $this->fieldModel->getEntityByAlias($alias);
if (!$field instanceof LeadField) {
throw new RecordNotFoundException($this->translator->trans('mautic.lead.contact.field.not.found', ['%alias%' => $alias], 'validators'));
}
return $field;
}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator;
use Symfony\Component\Validator\Constraint;
final class LeadFieldMinimumLength extends Constraint
{
public string $message = 'mautic.lead.field.char_length_limit.too_short';
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Mautic\LeadBundle\Entity\LeadField;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
final class LeadFieldMinimumLengthValidator extends ConstraintValidator
{
public function __construct(private Connection $connection)
{
}
public function validate(mixed $value, Constraint $constraint): void
{
if (!$value instanceof LeadField) {
throw new UnexpectedTypeException($value, LeadField::class);
}
if (!$constraint instanceof LeadFieldMinimumLength) {
throw new UnexpectedTypeException($constraint, LeadFieldMinimumLength::class);
}
if ($value->isNew() || !$value->supportsLength() || !$value->getCharLengthLimit()) {
return;
}
$maxCharacterLengthInUse = $this->getMaxCharacterLengthInUse($value);
if ($value->getCharLengthLimit() >= $maxCharacterLengthInUse) {
return;
}
$this->context->buildViolation($constraint->message, ['%length%' => $maxCharacterLengthInUse])
->atPath('charLengthLimit')
->addViolation();
}
private function getMaxCharacterLengthInUse(LeadField $leadField): int
{
try {
return (int) $this->connection->createQueryBuilder()
->select('MAX(CHAR_LENGTH('.$leadField->getAlias().'))')
->from(MAUTIC_TABLE_PREFIX.$leadField->getCustomFieldObject())
->executeQuery()
->fetchOne();
} catch (InvalidFieldNameException) {
return 0;
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Validator;
use Mautic\CoreBundle\Exception\RecordNotUnpublishedException;
use Mautic\LeadBundle\Entity\LeadList;
use Mautic\LeadBundle\Entity\LeadListRepository;
use Symfony\Contracts\Translation\TranslatorInterface;
class SegmentUsedInCampaignsValidator
{
public function __construct(private LeadListRepository $leadListRepository, private TranslatorInterface $translator)
{
}
/**
* @throws RecordNotUnpublishedException
*/
public function validate(LeadList $segment): void
{
if (!$segment->getId()) {
return;
}
$campaignNames = $this->leadListRepository->getSegmentCampaigns($segment->getId());
if (1 > count($campaignNames)) {
return;
}
$campaignNames = array_map(fn (string $segmentName): string => sprintf('"%s"', $segmentName), $campaignNames);
$errorMessage = $this->translator->trans(
'mautic.lead.lists.used_in_campaigns',
[
'%count%' => count($campaignNames),
'%campaignNames%' => implode(', ', $campaignNames),
],
'validators'
);
throw new RecordNotUnpublishedException($errorMessage);
}
}