Initial commit

This commit is contained in:
Julien Nahum
2022-09-20 21:59:52 +02:00
commit f8e6cd4dd6
479 changed files with 77078 additions and 0 deletions

View File

@@ -0,0 +1,533 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Support\Str;
class FormPropertyLogicRule implements Rule, DataAwareRule {
const ACTIONS_VALUES = [
'show-block',
'hide-block',
'make-it-optional',
'require-answer'
];
const CONDITION_MAPPING = [
'text' => [
'comparators' => [
'equals' => [
'expected_type' => 'string',
],
'does_not_equal' => [
'expected_type' => 'string',
],
'contains' => [
'expected_type' => 'string',
],
'does_not_contain' => [
'expected_type' => 'string',
],
'starts_with' => [
'expected_type' => 'string',
],
'ends_with' => [
'expected_type' => 'string',
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'url' => [
'comparators' => [
'equals' => [
'expected_type' => 'string',
],
'does_not_equal' => [
'expected_type' => 'string',
],
'contains' => [
'expected_type' => 'string',
],
'does_not_contain' => [
'expected_type' => 'string',
],
'starts_with' => [
'expected_type' => 'string',
],
'ends_with' => [
'expected_type' => 'string',
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'email' => [
'comparators' => [
'equals' => [
'expected_type' => 'string',
],
'does_not_equal' => [
'expected_type' => 'string',
],
'contains' => [
'expected_type' => 'string',
],
'does_not_contain' => [
'expected_type' => 'string',
],
'starts_with' => [
'expected_type' => 'string',
],
'ends_with' => [
'expected_type' => 'string',
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'phone_number' => [
'comparators' => [
'equals' => [
'expected_type' => 'string',
],
'does_not_equal' => [
'expected_type' => 'string',
],
'contains' => [
'expected_type' => 'string',
],
'does_not_contain' => [
'expected_type' => 'string',
],
'starts_with' => [
'expected_type' => 'string',
],
'ends_with' => [
'expected_type' => 'string',
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'number' => [
'comparators' => [
'equals' => [
'expected_type' => 'number',
],
'does_not_equal' => [
'expected_type' => 'number',
],
'greater_than' => [
'expected_type' => 'number',
],
'less_than' => [
'expected_type' => 'number',
],
'greater_than_or_equal_to' => [
'expected_type' => 'number',
],
'less_than_or_equal_to' => [
'expected_type' => 'number',
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'checkbox' => [
'comparators' => [
'equals' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'does_not_equal' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
]
],
'select' => [
'comparators' => [
'equals' => [
'expected_type' => 'string',
],
'does_not_equal' => [
'expected_type' => 'string',
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'multi_select' => [
'comparators' => [
'contains' => [
'expected_type' => 'object',
'format' => [
'type' => 'uuid',
]
],
'does_not_contain' => [
'expected_type' => 'object',
'format' => [
'type' => 'uuid',
]
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
'date' => [
'comparators' => [
'equals' => [
'expected_type' => 'string',
'format' => [
'type' => 'date'
]
],
'before' => [
'expected_type' => 'string',
'format' => [
'type' => 'date'
]
],
'after' => [
'expected_type' => 'string',
'format' => [
'type' => 'date'
]
],
'on_or_before' => [
'expected_type' => 'string',
'format' => [
'type' => 'date'
]
],
'on_or_after' => [
'expected_type' => 'string',
'format' => [
'type' => 'date'
]
],
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'past_week' => [
'expected_type' => 'object',
'format' => [
'type' => 'empty',
'values' => '{}'
]
],
'past_month' => [
'expected_type' => 'object',
'format' => [
'type' => 'empty',
'values' => '{}'
]
],
'past_year' => [
'expected_type' => 'object',
'format' => [
'type' => 'empty',
'values' => '{}'
]
],
'next_week' => [
'expected_type' => 'object',
'format' => [
'type' => 'empty',
'values' => '{}'
]
],
'next_month' => [
'expected_type' => 'object',
'format' => [
'type' => 'empty',
'values' => '{}'
]
],
'next_year' => [
'expected_type' => 'object',
'format' => [
'type' => 'empty',
'values' => '{}'
]
]
]
],
'files' => [
'comparators' => [
'is_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
],
'is_not_empty' => [
'expected_type' => 'boolean',
'format' => [
'type' => 'enum',
'values' => [true]
]
]
]
],
];
private $isConditionCorrect = true;
private $isActionCorrect = true;
private $field = [];
private $data = [];
private function checkBaseCondition($condtion) {
if (!isset($condtion['value'])) {
$this->isConditionCorrect = false;
return;
}
if (!isset($condtion['value']['property_meta'])) {
$this->isConditionCorrect = false;
return;
}
if (!isset($condtion['value']['property_meta']['type'])) {
$this->isConditionCorrect = false;
return;
}
if (!isset($condtion['value']['operator'])) {
$this->isConditionCorrect = false;
return;
}
if (!isset($condtion['value']['value'])) {
$this->isConditionCorrect = false;
return;
}
$typeField = $condtion['value']['property_meta']['type'];
$operator = $condtion['value']['operator'];
$value = $condtion['value']['value'];
if (!isset(self::CONDITION_MAPPING[$typeField])) {
$this->isConditionCorrect = false;
return;
}
if (!isset(self::CONDITION_MAPPING[$typeField]['comparators'][$operator])) {
$this->isConditionCorrect = false;
return;
}
$type = self::CONDITION_MAPPING[$typeField]['comparators'][$operator]['expected_type'];
// Type d'objet : string, boolean, number, object
if (
($type === 'string' && gettype($value) !== 'string') ||
($type === 'boolean' && !is_bool($value)) ||
($type === 'number' && !is_numeric($value)) ||
($type === 'object' && !is_array($value))
) {
$this->isConditionCorrect = false;
}
}
private function checkConditions($conditions) {
if (isset($conditions['operatorIdentifier'])) {
if (($conditions['operatorIdentifier'] !== 'and') && ($conditions['operatorIdentifier'] !== 'or')) {
$this->isConditionCorrect = false;
return;
}
if (isset($conditions['operatorIdentifier']['children'])) {
$this->isConditionCorrect = false;
return;
}
if (!is_array($conditions['children'])) {
$this->isConditionCorrect = false;
return;
}
foreach ($conditions['children'] as &$child) {
$this->checkConditions($child);
}
} else if (isset($conditions['identifier'])) {
$this->checkBaseCondition($conditions);
}
}
private function checkActions($conditions) {
if (is_array($conditions) && count($conditions) > 0) {
foreach($conditions as $val){
if (!in_array($val, static::ACTIONS_VALUES) ||
(in_array($this->field["type"], ['nf-text', 'nf-page-break', 'nf-divider', 'nf-image']) && !in_array($val, ['hide-block'])) ||
(isset($this->field["hidden"]) && $this->field["hidden"] && !in_array($val, ['show-block','require-answer'])) ||
(isset($this->field["required"]) && $this->field["required"] && !in_array($val, ['make-it-optional','hide-block']))
) {
$this->isActionCorrect = false;
break;
}
}
return;
}
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value) {
$this->setProperty($attribute);
if(isset($value["conditions"])){
$this->checkConditions($value["conditions"]);
}
if(isset($value["actions"])){
$this->checkActions($value["actions"]);
}
return ($this->isConditionCorrect && $this->isActionCorrect);
}
/**
* Get the validation error message.
*
*/
public function message() {
$errorList = [];
if(!$this->isConditionCorrect){
$errorList[] = "The logic conditions for ".$this->field['name']." are not complete.";
}
if(!$this->isActionCorrect){
$errorList[] = "The logic actions for ".$this->field['name']." are not valid.";
}
return $errorList;
}
/**
* Set the data under validation.
*
* @param array $data
* @return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
private function setProperty(string $attributeKey)
{
$attributeKey = Str::of($attributeKey)->replace('.logic','')->toString();
$this->field = \Arr::get($this->data, $attributeKey);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class OneEmailPerLine implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
if ($value === null || empty(trim($value))) return true;
foreach (preg_split("/\r\n|\n|\r/", $value) as $email) {
$email = trim($email);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return false;
}
}
return true;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'You need one valid email per line.';
}
}

68
app/Rules/StorageFile.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
namespace App\Rules;
use App\Http\Controllers\Forms\PublicFormController;
use App\Service\Storage\StorageFileNameParser;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class StorageFile implements Rule
{
public int $maxSize;
public string $error = 'Invalid file.';
/** @var string[] */
public array $fileTypes;
/**
* @param int $maxSize
* @param string[] $fileTypes
*/
public function __construct(int $maxSize, array $fileTypes = [])
{
$this->maxSize = $maxSize;
$this->fileTypes = $fileTypes;
}
/**
* File can have 2 formats:
* - file_name-{uuid}.{ext}
* - {uuid}
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value): bool
{
$fileNameParser = StorageFileNameParser::parse($value);
if (!$uuid = $fileNameParser->uuid) {
return false;
}
$filePath = PublicFormController::TMP_FILE_UPLOAD_PATH.$uuid;
if (!Storage::exists($filePath)) {
return false;
}
if (Storage::size($filePath) > $this->maxSize) {
$this->error = 'File is too large.';
return false;
}
if (count($this->fileTypes) > 0) {
$this->error = 'Incorrect file type. Allowed only: '.implode(",", $this->fileTypes);
return in_array($fileNameParser->extension, $this->fileTypes);
}
return true;
}
public function message(): string
{
return $this->error;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\ImplicitRule;
use Illuminate\Support\Facades\Http;
class ValidHCaptcha implements ImplicitRule
{
const H_CAPTCHA_VERIFY_URL = "https://hcaptcha.com/siteverify";
private $error = 'Invalid CAPTCHA. Please prove you\'re not a bot.';
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
if (empty($value)) {
$this->error = "Please complete the captcha.";
return false;
}
return Http::asForm()->post(self::H_CAPTCHA_VERIFY_URL, [
'secret' => config('services.h_captcha.secret_key'),
'response' => $value
])->json('success');
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return $this->error;
}
}