Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Helper;
|
||||
|
||||
use Mautic\CoreBundle\Helper\AbstractFormFieldHelper;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Symfony\Component\Validator\Constraints\Blank;
|
||||
use Symfony\Component\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\EqualTo;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\Url;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class FormFieldHelper extends AbstractFormFieldHelper
|
||||
{
|
||||
private ValidatorInterface $validator;
|
||||
|
||||
private array $types = [
|
||||
'captcha' => [
|
||||
'constraints' => [
|
||||
NotBlank::class => ['message' => 'mautic.form.submission.captcha.invalid'],
|
||||
|
||||
EqualTo::class => ['message' => 'mautic.form.submission.captcha.invalid'],
|
||||
|
||||
Blank::class => ['message' => 'mautic.form.submission.captcha.invalid'],
|
||||
],
|
||||
],
|
||||
'checkboxgrp' => [],
|
||||
'country' => [],
|
||||
'date' => [],
|
||||
'datetime' => [],
|
||||
'email' => [
|
||||
'filter' => 'email',
|
||||
'constraints' => [
|
||||
Email::class => ['message' => 'mautic.form.submission.email.invalid'],
|
||||
],
|
||||
],
|
||||
'freetext' => [],
|
||||
'freehtml' => [],
|
||||
'hidden' => [],
|
||||
'companyLookup' => [],
|
||||
'number' => [
|
||||
'filter' => 'float',
|
||||
],
|
||||
'slider' => [
|
||||
'filter' => 'float',
|
||||
],
|
||||
'pagebreak' => [],
|
||||
'password' => [],
|
||||
'radiogrp' => [],
|
||||
'select' => [],
|
||||
'tel' => [],
|
||||
'text' => [],
|
||||
'textarea' => [],
|
||||
'url' => [
|
||||
'filter' => 'url',
|
||||
'constraints' => [
|
||||
Url::class => ['message' => 'mautic.form.submission.url.invalid'],
|
||||
],
|
||||
],
|
||||
'file' => [],
|
||||
];
|
||||
|
||||
public function __construct(Translator $translator, ?ValidatorInterface $validator = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
|
||||
if (null === $validator) {
|
||||
$validator = $validator = Validation::createValidator();
|
||||
}
|
||||
$this->validator = $validator;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the translation key prefix.
|
||||
*/
|
||||
public function setTranslationKeyPrefix(): void
|
||||
{
|
||||
$this->translationKeyPrefix = 'mautic.form.field.type.';
|
||||
}
|
||||
|
||||
public function getTypes(): array
|
||||
{
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
public function getFieldFilter(string $type): string
|
||||
{
|
||||
return $this->types[$type]['filter'] ?? 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Field $f
|
||||
*/
|
||||
public function validateFieldValue($type, $value, $f = null): array
|
||||
{
|
||||
$errors = [];
|
||||
if (isset($this->types[$type]['constraints'])) {
|
||||
foreach ($this->types[$type]['constraints'] as $constraint => $opts) {
|
||||
// don't check empty values unless the constraint is NotBlank
|
||||
if (NotBlank::class === $constraint && empty($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('captcha' == $type) {
|
||||
$captcha = $f->getProperties()['captcha'];
|
||||
if (empty($captcha) && Blank::class !== $constraint) {
|
||||
// Used as a honeypot
|
||||
$captcha = '';
|
||||
} elseif (Blank::class === $constraint) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (EqualTo::class == $constraint) {
|
||||
$opts['value'] = $captcha;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var ConstraintViolationList $violations */
|
||||
$violations = $this->validator->validate($value, new $constraint($opts));
|
||||
|
||||
if (count($violations)) {
|
||||
/** @var ConstraintViolation $v */
|
||||
foreach ($violations as $v) {
|
||||
$transParameters = $v->getParameters();
|
||||
|
||||
if (null !== $f) {
|
||||
$transParameters['%label%'] = '"'.$f->getLabel().'"';
|
||||
}
|
||||
|
||||
$errors[] = $this->translator->trans($v->getMessage(), $transParameters, 'validators');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and replace the HTML of the form field with the value.
|
||||
*/
|
||||
public function populateField($field, $value, $formName, &$formHtml): void
|
||||
{
|
||||
$alias = $field->getAlias();
|
||||
|
||||
// Adds the "readonly" attribute to a field if it is configured as read-only with auto-fill enabled and a sanitized value exists.
|
||||
$fieldAttributeReadOnly = fn ($field, $sanitizedValue) => ($field->isAutoFillReadOnly() && $sanitizedValue) ? ' readonly ' : '';
|
||||
|
||||
switch ($field->getType()) {
|
||||
case 'text':
|
||||
case 'number':
|
||||
case 'email':
|
||||
case 'hidden':
|
||||
case 'tel':
|
||||
case 'url':
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
if ('tel' === $field->getType()) {
|
||||
$sanitizedValue = InputHelper::clean($value);
|
||||
} else {
|
||||
$sanitizedValue = $this->sanitizeValue($value);
|
||||
}
|
||||
if (preg_match('/<input(.*?)value="(.*?)"(.*?)id="mauticform_input_'.$formName.'_'.$alias.'"(.*?)\/?>/i', $formHtml, $match)) {
|
||||
$replace = '<input'.$match[1].'id="mauticform_input_'.$formName.'_'.$alias.'"'.$match[3].'value="'.$sanitizedValue.'"'
|
||||
.$match[4].$fieldAttributeReadOnly($field, $sanitizedValue).'/>';
|
||||
$formHtml = str_replace($match[0], $replace, $formHtml);
|
||||
}
|
||||
break;
|
||||
case 'textarea':
|
||||
if (preg_match('/<textarea(.*?)id="mauticform_input_'.$formName.'_'.$alias.'"(.*?)>(.*?)<\/textarea>/i', $formHtml, $match)) {
|
||||
$value = $this->sanitizeValue($value);
|
||||
$replace = '<textarea'.$match[1].'id="mauticform_input_'.$formName.'_'.$alias.'"'.$match[2].$fieldAttributeReadOnly($field, $value).'>'.$value.'</textarea>';
|
||||
$formHtml = str_replace($match[0], $replace, $formHtml);
|
||||
}
|
||||
break;
|
||||
case 'checkboxgrp':
|
||||
$isUrlEncoded = is_string($value) && str_contains($value, '%7C');
|
||||
$separator = $isUrlEncoded ? urlencode('|') : '|';
|
||||
if (is_string($value) && strrpos($value, $separator) > 0) {
|
||||
$value = explode($separator, $value);
|
||||
} elseif (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
foreach ($value as $val) {
|
||||
$val = $this->sanitizeValue($val);
|
||||
if (preg_match(
|
||||
'/<input(.*?)id="mauticform_checkboxgrp_checkbox_'.$alias.'(.*?)"(.*?)value="'.$val.'"(.*?)\/?>/i',
|
||||
$formHtml,
|
||||
$match
|
||||
)) {
|
||||
$replace = '<input'.$match[1].'id="mauticform_checkboxgrp_checkbox_'.$alias.$match[2].'"'.$match[3].'value="'.$val.'"'
|
||||
.$match[4].' checked />';
|
||||
$formHtml = str_replace($match[0], $replace, $formHtml);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'radiogrp':
|
||||
$value = $this->sanitizeValue($value);
|
||||
if (preg_match('/<input(.*?)id="mauticform_radiogrp_radio_'.$alias.'(.*?)"(.*?)value="'.$value.'"(.*?)\/?>/i', $formHtml, $match)) {
|
||||
$replace = '<input'.$match[1].'id="mauticform_radiogrp_radio_'.$alias.$match[2].'"'.$match[3].'value="'.$value.'"'.$match[4]
|
||||
.' checked />';
|
||||
$formHtml = str_replace($match[0], $replace, $formHtml);
|
||||
}
|
||||
break;
|
||||
case 'select':
|
||||
case 'country':
|
||||
$regex = '/<select\s*id="mauticform_input_'.$formName.'_'.$alias.'"(.*?)<\/select>/is';
|
||||
if (preg_match($regex, $formHtml, $match)) {
|
||||
$origText = $match[0];
|
||||
$replace = str_replace(
|
||||
'<option value="'.$this->sanitizeValue($value).'">',
|
||||
'<option value="'.$this->sanitizeValue($value).'" selected="selected">',
|
||||
$origText
|
||||
);
|
||||
$formHtml = str_replace($origText, $replace, $formHtml);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function sanitizeValue($value)
|
||||
{
|
||||
$valueType = gettype($value);
|
||||
$value = str_replace(['"', '>', '<'], ['"', '>', '<'], strip_tags(rawurldecode($value)));
|
||||
// for boolean expect 0 or 1
|
||||
if ('boolean' === $valueType) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Helper;
|
||||
|
||||
use Mautic\CoreBundle\Exception\FileUploadException;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\FileUploader;
|
||||
use Mautic\FormBundle\Crate\UploadFileCrate;
|
||||
use Mautic\FormBundle\Entity\Field;
|
||||
use Mautic\FormBundle\Entity\Form;
|
||||
use Mautic\FormBundle\Entity\Submission;
|
||||
|
||||
class FormUploader
|
||||
{
|
||||
public function __construct(
|
||||
private FileUploader $fileUploader,
|
||||
private CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileUploadException
|
||||
*/
|
||||
public function uploadFiles(UploadFileCrate $filesToUpload, Submission $submission): void
|
||||
{
|
||||
$uploadedFiles = [];
|
||||
$result = $submission->getResults();
|
||||
$alias = ''; // Only for IDE - will be overriden by foreach
|
||||
|
||||
try {
|
||||
foreach ($filesToUpload as $fileFieldCrate) {
|
||||
$field = $fileFieldCrate->getField();
|
||||
$alias = $field->getAlias();
|
||||
$uploadDir = $this->getUploadDir($field);
|
||||
$fileName = $this->fileUploader->upload($uploadDir, $fileFieldCrate->getUploadedFile());
|
||||
$result[$alias] = $fileName;
|
||||
$uploadedFile = $uploadDir.DIRECTORY_SEPARATOR.$fileName;
|
||||
$this->fixRotationJPG($uploadedFile);
|
||||
$uploadedFiles[] =$uploadedFile;
|
||||
}
|
||||
$submission->setResults($result);
|
||||
} catch (FileUploadException) {
|
||||
foreach ($uploadedFiles as $filePath) {
|
||||
$this->fileUploader->delete($filePath);
|
||||
}
|
||||
throw new FileUploadException($alias);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*/
|
||||
public function getCompleteFilePath(Field $field, $fileName): string
|
||||
{
|
||||
$uploadDir = $this->getUploadDir($field);
|
||||
|
||||
return $uploadDir.DIRECTORY_SEPARATOR.$fileName;
|
||||
}
|
||||
|
||||
public function deleteAllFilesOfFormField(Field $field): void
|
||||
{
|
||||
if (!$field->isFileType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$uploadDir = $this->getUploadDir($field);
|
||||
$this->fileUploader->delete($uploadDir);
|
||||
}
|
||||
|
||||
public function deleteFilesOfForm(Form $form): void
|
||||
{
|
||||
$formId = $form->getId() ?: $form->deletedId;
|
||||
$formUploadDir = $this->getUploadDirOfForm($formId);
|
||||
$this->fileUploader->delete($formUploadDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Refactor code that result can be accessed normally and not only as a array of values
|
||||
*/
|
||||
public function deleteUploadedFiles(Submission $submission): void
|
||||
{
|
||||
$fields = $submission->getForm()->getFields();
|
||||
foreach ($fields as $field) {
|
||||
$this->deleteFileOfFormField($submission, $field);
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteFileOfFormField(Submission $submission, Field $field): void
|
||||
{
|
||||
$alias = $field->getAlias();
|
||||
$results = $submission->getResults();
|
||||
|
||||
if (!$field->isFileType() || empty($results[$alias])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fileName = $results[$alias];
|
||||
$filePath = $this->getCompleteFilePath($field, $fileName);
|
||||
$this->fileUploader->delete($filePath);
|
||||
}
|
||||
|
||||
private function getUploadDir(Field $field): string
|
||||
{
|
||||
$fieldId = $field->getId();
|
||||
$formUploadDir = $this->getUploadDirOfForm($field->getForm()->getId());
|
||||
|
||||
return $formUploadDir.DIRECTORY_SEPARATOR.$fieldId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \LogicException If formId is null
|
||||
*/
|
||||
private function getUploadDirOfForm(int $formId): string
|
||||
{
|
||||
$uploadDir = $this->coreParametersHelper->get('form_upload_dir');
|
||||
|
||||
return $uploadDir.DIRECTORY_SEPARATOR.$formId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix iOS picture orientation after upload PHP
|
||||
* https://stackoverflow.com/questions/22308921/fix-ios-picture-orientation-after-upload-php.
|
||||
*/
|
||||
private function fixRotationJPG($filename): void
|
||||
{
|
||||
if (IMAGETYPE_JPEG != exif_imagetype($filename)) {
|
||||
return;
|
||||
}
|
||||
$exif = exif_read_data($filename);
|
||||
if (empty($exif['Orientation'])) {
|
||||
return;
|
||||
}
|
||||
$ort = $exif['Orientation']; /* STORES ORIENTATION FROM IMAGE */
|
||||
$ort1 = $ort;
|
||||
if (!empty($ort1)) {
|
||||
$image = imagecreatefromjpeg($filename);
|
||||
$ort = $ort1;
|
||||
switch ($ort) {
|
||||
case 3:
|
||||
$image = imagerotate($image, 180, 0);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
$image = imagerotate($image, -90, 0);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
$image = imagerotate($image, 90, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
imagejpeg($image, $filename, 90);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Helper;
|
||||
|
||||
class PointActionHelper
|
||||
{
|
||||
public static function validateFormSubmit($eventDetails, $action): bool
|
||||
{
|
||||
$form = $eventDetails->getForm();
|
||||
$formId = $form->getId();
|
||||
$limitToForms = $action['properties']['forms'];
|
||||
|
||||
if (!empty($limitToForms) && !in_array($formId, $limitToForms)) {
|
||||
// no points change
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Helper;
|
||||
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
|
||||
class PropertiesAccessor
|
||||
{
|
||||
public function __construct(
|
||||
private FormModel $formModel,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $field
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getProperties(array $field)
|
||||
{
|
||||
$hasContactFieldMapped = !empty($field['mappedField']) && !empty($field['mappedObject']) && 'contact' === $field['mappedObject'];
|
||||
if ('country' === $field['type'] || ($hasContactFieldMapped && !empty($field['properties']['syncList']))) {
|
||||
return $this->formModel->getContactFieldPropertiesList((string) $field['mappedField']);
|
||||
} elseif (!empty($field['properties'])) {
|
||||
return $this->getOptionsListFromProperties($field['properties']);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|mixed[] $options
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getChoices($options): array
|
||||
{
|
||||
$choices = [];
|
||||
|
||||
if (is_array($options) && !isset($options[0]['value'])) {
|
||||
return array_flip($options);
|
||||
}
|
||||
|
||||
if (!is_array($options)) {
|
||||
$options = explode('|', (string) $options);
|
||||
}
|
||||
|
||||
foreach ($options as $option) {
|
||||
if (is_array($option)) {
|
||||
if (isset($option['label']) && isset($option['alias'])) {
|
||||
$choices[$option['label']] = $option['alias'];
|
||||
} elseif (isset($option['label']) && isset($option['value'])) {
|
||||
$choices[$option['label']] = $option['value'];
|
||||
} else {
|
||||
foreach ($option as $opt) {
|
||||
$choices[$opt] = $opt;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$choices[$option] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $properties
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getOptionsListFromProperties(array $properties)
|
||||
{
|
||||
if (!empty($properties['list']['list'])) {
|
||||
return $properties['list']['list'];
|
||||
} elseif (!empty($properties['optionlist']['list'])) {
|
||||
return $properties['optionlist']['list'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Helper;
|
||||
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\FormBundle\Model\FormModel;
|
||||
|
||||
class TokenHelper
|
||||
{
|
||||
public const REGEX = '/{form=(.*?)}/';
|
||||
|
||||
public function __construct(
|
||||
protected FormModel $formModel,
|
||||
protected CorePermissions $security,
|
||||
) {
|
||||
}
|
||||
|
||||
public function findFormTokens($content): array
|
||||
{
|
||||
$tokens = [];
|
||||
|
||||
preg_match_all(self::REGEX, $content, $matches);
|
||||
|
||||
if (count($matches[0])) {
|
||||
foreach ($matches[1] as $k => $id) {
|
||||
$token = $matches[0][$k];
|
||||
|
||||
if (isset($tokens[$token])) {
|
||||
continue;
|
||||
}
|
||||
$form = $this->formModel->getEntity($id);
|
||||
if (null !== $form
|
||||
&& (
|
||||
$form->isPublished(false)
|
||||
|| $this->security->hasEntityAccess(
|
||||
'form:forms:viewown', 'form:forms:viewother', $form->getCreatedBy()
|
||||
)
|
||||
)
|
||||
) {
|
||||
$formHtml = ($form->isPublished()) ? $this->formModel->getContent($form) :
|
||||
'';
|
||||
|
||||
// pouplate get parameters
|
||||
$this->formModel->populateValuesWithGetParameters($form, $formHtml);
|
||||
|
||||
$tokens[$token] = $formHtml;
|
||||
} else {
|
||||
$tokens[$token] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user