2022-09-20 19:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Http\Requests;
|
|
|
|
|
|
|
|
use App\Models\Forms\Form;
|
|
|
|
use App\Rules\StorageFile;
|
2024-02-23 10:54:12 +00:00
|
|
|
use App\Rules\ValidHCaptcha;
|
|
|
|
use App\Rules\ValidPhoneInputRule;
|
|
|
|
use App\Rules\ValidUrl;
|
2022-09-20 19:59:52 +00:00
|
|
|
use App\Service\Forms\FormLogicPropertyResolver;
|
|
|
|
use Illuminate\Foundation\Http\FormRequest;
|
2024-02-23 10:54:12 +00:00
|
|
|
use Illuminate\Http\Request;
|
2022-09-20 19:59:52 +00:00
|
|
|
use Illuminate\Support\Str;
|
|
|
|
use Illuminate\Validation\Rule;
|
|
|
|
|
|
|
|
class AnswerFormRequest extends FormRequest
|
|
|
|
{
|
|
|
|
public Form $form;
|
|
|
|
|
|
|
|
protected array $requestRules = [];
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
protected int $maxFileSize;
|
|
|
|
|
|
|
|
public function __construct(Request $request)
|
|
|
|
{
|
|
|
|
$this->form = $request->form;
|
2023-11-01 15:58:10 +00:00
|
|
|
$this->maxFileSize = $this->form->workspace->max_file_size;
|
2022-09-20 19:59:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validate form before use it
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function authorize()
|
|
|
|
{
|
2024-02-23 10:54:12 +00:00
|
|
|
return ! $this->form->is_closed && ! $this->form->max_number_of_submissions_reached && $this->form->visibility === 'public';
|
2022-09-20 19:59:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the validation rules that apply to the form.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function rules()
|
|
|
|
{
|
|
|
|
foreach ($this->form->properties as $property) {
|
|
|
|
$rules = [];
|
|
|
|
|
2023-08-30 07:58:29 +00:00
|
|
|
/*if (!$this->form->is_pro) { // If not pro then not check logic
|
2022-09-20 19:59:52 +00:00
|
|
|
$property['logic'] = false;
|
2023-08-30 07:58:29 +00:00
|
|
|
}*/
|
2022-09-20 19:59:52 +00:00
|
|
|
|
|
|
|
// For get values instead of Id for select/multi select options
|
|
|
|
$data = $this->toArray();
|
|
|
|
$selectionFields = collect($this->form->properties)->filter(function ($pro) {
|
|
|
|
return in_array($pro['type'], ['select', 'multi_select']);
|
|
|
|
});
|
2024-02-23 10:54:12 +00:00
|
|
|
foreach ($selectionFields as $field) {
|
|
|
|
if (isset($data[$field['id']]) && is_array($data[$field['id']])) {
|
2022-09-20 19:59:52 +00:00
|
|
|
$data[$field['id']] = array_map(function ($val) use ($field) {
|
|
|
|
$tmpop = collect($field[$field['type']]['options'])->first(function ($op) use ($val) {
|
2024-02-23 10:54:12 +00:00
|
|
|
return $op['id'] ?? $op['value'] === $val;
|
2022-09-20 19:59:52 +00:00
|
|
|
});
|
2024-02-23 10:54:12 +00:00
|
|
|
|
|
|
|
return isset($tmpop['name']) ? $tmpop['name'] : '';
|
2022-09-20 19:59:52 +00:00
|
|
|
}, $data[$field['id']]);
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
}
|
2022-09-20 19:59:52 +00:00
|
|
|
if (FormLogicPropertyResolver::isRequired($property, $data)) {
|
|
|
|
$rules[] = 'required';
|
|
|
|
|
|
|
|
if ($property['type'] == 'checkbox') {
|
2024-01-17 13:52:32 +00:00
|
|
|
// Required for checkboxes means true
|
2022-09-20 19:59:52 +00:00
|
|
|
$rules[] = 'accepted';
|
2024-02-23 10:54:12 +00:00
|
|
|
} elseif ($property['type'] == 'number' && isset($property['is_rating']) && $property['is_rating']) {
|
2024-01-17 13:52:32 +00:00
|
|
|
// For star rating, needs a minimum of 1 star
|
|
|
|
$rules[] = 'min:1';
|
2022-09-20 19:59:52 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$rules[] = 'nullable';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean id to escape "."
|
|
|
|
$propertyId = $property['id'];
|
|
|
|
if (in_array($property['type'], ['multi_select'])) {
|
|
|
|
$rules[] = 'array';
|
|
|
|
$this->requestRules[$propertyId.'.*'] = $this->getPropertyRules($property);
|
|
|
|
} else {
|
|
|
|
$rules = array_merge($rules, $this->getPropertyRules($property));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->requestRules[$propertyId] = $rules;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate hCaptcha
|
2023-08-30 07:58:29 +00:00
|
|
|
if ($this->form->use_captcha) {
|
2022-09-20 19:59:52 +00:00
|
|
|
$this->requestRules['h-captcha-response'] = [new ValidHCaptcha()];
|
|
|
|
}
|
2023-01-10 13:52:14 +00:00
|
|
|
|
|
|
|
// Validate submission_id for edit mode
|
2023-08-30 07:58:29 +00:00
|
|
|
if ($this->form->is_pro && $this->form->editable_submissions) {
|
2023-01-10 13:52:14 +00:00
|
|
|
$this->requestRules['submission_id'] = 'string';
|
|
|
|
}
|
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return $this->requestRules;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renames validated fields (because field names are ids)
|
2024-02-23 10:54:12 +00:00
|
|
|
*
|
2022-09-20 19:59:52 +00:00
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function attributes()
|
|
|
|
{
|
|
|
|
$fields = [];
|
|
|
|
foreach ($this->form->properties as $property) {
|
|
|
|
$fields[$property['id']] = $property['name'];
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return $fields;
|
|
|
|
}
|
|
|
|
|
2023-02-13 08:50:19 +00:00
|
|
|
/**
|
|
|
|
* Get the validation messages that apply to the request.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function messages()
|
|
|
|
{
|
|
|
|
$messages = [];
|
|
|
|
foreach ($this->form->properties as $property) {
|
2024-02-23 10:54:12 +00:00
|
|
|
if ($property['type'] == 'date' && isset($property['date_range']) && $property['date_range']) {
|
|
|
|
$messages[$property['id'].'.0.required_with'] = 'From date is required';
|
|
|
|
$messages[$property['id'].'.1.required_with'] = 'To date is required';
|
|
|
|
$messages[$property['id'].'.0.before_or_equal'] = 'From date must be before or equal To date';
|
2023-02-13 08:50:19 +00:00
|
|
|
}
|
2023-03-22 14:49:00 +00:00
|
|
|
if ($property['type'] == 'number' && isset($property['is_rating']) && $property['is_rating']) {
|
2024-02-23 10:54:12 +00:00
|
|
|
$messages[$property['id'].'.min'] = 'A rating must be selected';
|
2023-03-22 14:49:00 +00:00
|
|
|
}
|
2023-02-13 08:50:19 +00:00
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2023-02-13 08:50:19 +00:00
|
|
|
return $messages;
|
|
|
|
}
|
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
/**
|
|
|
|
* Return validation rules for a given form property
|
|
|
|
*/
|
|
|
|
private function getPropertyRules($property): array
|
|
|
|
{
|
|
|
|
switch ($property['type']) {
|
|
|
|
case 'text':
|
2022-12-22 10:53:33 +00:00
|
|
|
case 'signature':
|
2022-09-20 19:59:52 +00:00
|
|
|
return ['string'];
|
|
|
|
case 'number':
|
2023-03-22 14:49:00 +00:00
|
|
|
if ($property['is_rating'] ?? false) {
|
2024-01-17 13:52:32 +00:00
|
|
|
return ['numeric'];
|
2023-03-22 14:49:00 +00:00
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return ['numeric'];
|
|
|
|
case 'select':
|
|
|
|
case 'multi_select':
|
2023-08-30 07:58:29 +00:00
|
|
|
if (($property['allow_creation'] ?? false)) {
|
2022-09-20 19:59:52 +00:00
|
|
|
return ['string'];
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return [Rule::in($this->getSelectPropertyOptions($property))];
|
|
|
|
case 'checkbox':
|
|
|
|
return ['boolean'];
|
|
|
|
case 'url':
|
|
|
|
if (isset($property['file_upload']) && $property['file_upload']) {
|
2023-04-26 15:05:02 +00:00
|
|
|
$this->requestRules[$property['id'].'.*'] = [new StorageFile($this->maxFileSize, [], $this->form)];
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return ['array'];
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
|
|
|
return [new ValidUrl()];
|
2022-09-20 19:59:52 +00:00
|
|
|
case 'files':
|
|
|
|
$allowedFileTypes = [];
|
2024-02-23 10:54:12 +00:00
|
|
|
if (! empty($property['allowed_file_types'])) {
|
|
|
|
$allowedFileTypes = explode(',', $property['allowed_file_types']);
|
2022-09-20 19:59:52 +00:00
|
|
|
}
|
2023-04-26 15:05:02 +00:00
|
|
|
$this->requestRules[$property['id'].'.*'] = [new StorageFile($this->maxFileSize, $allowedFileTypes, $this->form)];
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return ['array'];
|
|
|
|
case 'email':
|
|
|
|
return ['email:filter'];
|
|
|
|
case 'date':
|
|
|
|
if (isset($property['date_range']) && $property['date_range']) {
|
2022-11-06 11:34:19 +00:00
|
|
|
$this->requestRules[$property['id'].'.*'] = $this->getRulesForDate($property);
|
2023-02-13 08:50:19 +00:00
|
|
|
$this->requestRules[$property['id'].'.0'] = ['required_with:'.$property['id'].'.1', 'before_or_equal:'.$property['id'].'.1'];
|
|
|
|
$this->requestRules[$property['id'].'.1'] = ['required_with:'.$property['id'].'.0'];
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-11-06 11:34:19 +00:00
|
|
|
return ['array', 'min:2'];
|
2022-09-20 19:59:52 +00:00
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-11-06 11:34:19 +00:00
|
|
|
return $this->getRulesForDate($property);
|
2023-09-12 08:13:10 +00:00
|
|
|
case 'phone_number':
|
2023-09-20 10:14:08 +00:00
|
|
|
if (isset($property['use_simple_text_input']) && $property['use_simple_text_input']) {
|
|
|
|
return ['string'];
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
|
|
|
return ['string', 'min:6', new ValidPhoneInputRule()];
|
2022-09-20 19:59:52 +00:00
|
|
|
default:
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-06 11:34:19 +00:00
|
|
|
private function getRulesForDate($property)
|
|
|
|
{
|
|
|
|
if (isset($property['disable_past_dates']) && $property['disable_past_dates']) {
|
|
|
|
return ['date', 'after_or_equal:today'];
|
2024-02-23 10:54:12 +00:00
|
|
|
} elseif (isset($property['disable_future_dates']) && $property['disable_future_dates']) {
|
2022-11-06 11:34:19 +00:00
|
|
|
return ['date', 'before_or_equal:today'];
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-11-06 11:34:19 +00:00
|
|
|
return ['date'];
|
|
|
|
}
|
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
private function getSelectPropertyOptions($property): array
|
|
|
|
{
|
|
|
|
$type = $property['type'];
|
2024-02-23 10:54:12 +00:00
|
|
|
if (! isset($property[$type])) {
|
2022-09-20 19:59:52 +00:00
|
|
|
return [];
|
|
|
|
}
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return array_column($property[$type]['options'], 'name');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function prepareForValidation()
|
|
|
|
{
|
|
|
|
$receivedData = $this->toArray();
|
|
|
|
$mergeData = [];
|
2023-10-03 15:50:46 +00:00
|
|
|
$countryCodeMapper = json_decode(file_get_contents(resource_path('data/country_code_mapper.json')), true);
|
|
|
|
collect($this->form->properties)->each(function ($property) use ($countryCodeMapper, $receivedData, &$mergeData) {
|
2022-09-20 19:59:52 +00:00
|
|
|
$receivedValue = $receivedData[$property['id']] ?? null;
|
2023-10-03 15:50:46 +00:00
|
|
|
|
|
|
|
// Escape all '\' in select options
|
2024-02-23 10:54:12 +00:00
|
|
|
if (in_array($property['type'], ['select', 'multi_select']) && ! is_null($receivedValue)) {
|
2022-09-20 19:59:52 +00:00
|
|
|
if (is_array($receivedValue)) {
|
|
|
|
$mergeData[$property['id']] = collect($receivedValue)->map(function ($value) {
|
|
|
|
$value = Str::of($value);
|
2024-02-23 10:54:12 +00:00
|
|
|
|
2022-09-20 19:59:52 +00:00
|
|
|
return $value->replace(
|
2024-02-23 10:54:12 +00:00
|
|
|
["\e", "\f", "\n", "\r", "\t", "\v", '\\'],
|
|
|
|
['\\e', '\\f', '\\n', '\\r', '\\t', '\\v', '\\\\']
|
2022-09-20 19:59:52 +00:00
|
|
|
)->toString();
|
|
|
|
})->toArray();
|
|
|
|
} else {
|
|
|
|
$receivedValue = Str::of($receivedValue);
|
|
|
|
$mergeData[$property['id']] = $receivedValue->replace(
|
2024-02-23 10:54:12 +00:00
|
|
|
["\e", "\f", "\n", "\r", "\t", "\v", '\\'],
|
|
|
|
['\\e', '\\f', '\\n', '\\r', '\\t', '\\v', '\\\\']
|
2022-09-20 19:59:52 +00:00
|
|
|
)->toString();
|
|
|
|
}
|
|
|
|
}
|
2023-10-03 15:50:46 +00:00
|
|
|
|
2024-02-23 10:54:12 +00:00
|
|
|
if ($property['type'] === 'phone_number' && (! isset($property['use_simple_text_input']) || ! $property['use_simple_text_input']) && $receivedValue && in_array($receivedValue, $countryCodeMapper)) {
|
2023-10-03 15:50:46 +00:00
|
|
|
$mergeData[$property['id']] = null;
|
|
|
|
}
|
2022-09-20 19:59:52 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
$this->merge($mergeData);
|
|
|
|
}
|
|
|
|
}
|