opnform-host-nginx/app/Service/Forms/FormCleaner.php

290 lines
8.0 KiB
PHP
Raw Normal View History

2022-09-20 21:59:52 +02:00
<?php
namespace App\Service\Forms;
use App\Http\Requests\UserFormRequest;
use App\Http\Resources\FormResource;
use App\Models\Forms\Form;
use App\Models\User;
2024-02-23 11:54:12 +01:00
use App\Models\Workspace;
2022-09-20 21:59:52 +02:00
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Stevebauman\Purify\Facades\Purify;
2024-02-23 11:54:12 +01:00
2022-09-20 21:59:52 +02:00
use function collect;
class FormCleaner
{
/**
* All the performed cleanings
2024-02-23 11:54:12 +01:00
*
2022-09-20 21:59:52 +02:00
* @var bool
*/
private array $cleanings = [];
private array $data;
// For remove keys those have empty value
private array $customKeys = ['seo_meta'];
2022-09-20 21:59:52 +02:00
private array $formDefaults = [
'no_branding' => false,
'database_fields_update' => null,
'editable_submissions' => false,
'custom_code' => null,
2024-02-23 11:54:12 +01:00
'seo_meta' => [],
'redirect_url' => null
2022-09-20 21:59:52 +02:00
];
private array $formNonTrialingDefaults = [
// Custom code protection disabled for now
// 'custom_code' => null,
];
2022-09-20 21:59:52 +02:00
private array $fieldDefaults = [
// 'name' => '' TODO: prevent name changing, use alias for column and keep original name as it is
'file_upload' => false,
];
private array $cleaningMessages = [
// For form
2024-02-23 11:54:12 +01:00
'no_branding' => 'OpenForm branding is not hidden.',
2022-09-20 21:59:52 +02:00
'database_fields_update' => 'Form submission will only create new records (no updates).',
'editable_submissions' => 'Users will not be able to edit their submissions.',
'custom_code' => 'Custom code was disabled',
'seo_meta' => 'Custom SEO was disabled',
'redirect_url' => 'Redirect Url was disabled',
2022-09-20 21:59:52 +02:00
// For fields
2024-02-23 11:54:12 +01:00
'file_upload' => 'Link field is not a file upload.',
2022-09-20 21:59:52 +02:00
'custom_block' => 'The custom block was removed.',
];
/**
* Returns form data after request ingestion
*/
public function getData(): array
{
return $this->data;
}
/**
* Returns true if at least one cleaning was done
*/
public function hasCleaned(): bool
{
return count($this->cleanings) > 0;
}
/**
* Returns the messages for each cleaning step performed
*/
public function getPerformedCleanings(): array
{
$cleaningMsgs = [];
foreach ($this->cleanings as $key => $val) {
$cleaningMsgs[$key] = collect($val)->map(function ($cleaning) {
return $this->cleaningMessages[$cleaning];
});
}
2024-02-23 11:54:12 +01:00
2022-09-20 21:59:52 +02:00
return $cleaningMsgs;
}
/**
* Removes form pro features from data if user isn't pro
*/
public function processRequest(UserFormRequest $request): FormCleaner
{
$data = $request->validated();
$this->data = $this->commonCleaning($data);
return $this;
}
/**
* Create form cleaner instance from existing form
*/
2024-02-23 11:54:12 +01:00
public function processForm(Request $request, Form $form): FormCleaner
{
2022-09-20 21:59:52 +02:00
$data = (new FormResource($form))->toArray($request);
$this->data = $this->commonCleaning($data);
return $this;
}
2024-02-23 11:54:12 +01:00
private function isPro(Workspace $workspace)
{
2022-09-20 21:59:52 +02:00
return $workspace->is_pro;
}
private function isTrialing(Workspace $workspace)
{
return $workspace->is_trialing;
}
2022-09-20 21:59:52 +02:00
/**
* Dry run celanings
2024-02-23 11:54:12 +01:00
*
2022-09-20 21:59:52 +02:00
* @param User|null $user
*/
2024-02-23 11:54:12 +01:00
public function simulateCleaning(Workspace $workspace): FormCleaner
{
if ($this->isTrialing($workspace)) {
$this->data = $this->removeNonTrialingFeatures($this->data, true);
}
Notification & Integrations refactoring (#346) * Integrations Refactoring - WIP * integrations list & edit - WIP * Fix integration store binding issue * integrations refactor - WIP * Form integration - WIP * Form integration Edit - WIP * Integration Refactor - Slack - WIP * Integration Refactor - Discord - WIP * Integration Refactor - Webhook - WIP * Integration Refactor - Send Submission Confirmation - WIP * Integration Refactor - Backend handler - WIP * Form Integration Status field * Integration Refactor - Backend SubmissionConfirmation - WIP * IntegrationMigration Command * skip confirmation email test case * Small refactoring * FormIntegration status active/inactive * formIntegrationData to integrationData * Rename file name with Integration suffix for integration realted files * Loader on form integrations * WIP * form integration test case * WIP * Added Integration card - working on refactoring * change location for IntegrationCard and update package file * Form Integration Create/Edit in single Modal * Remove integration extra pages * crisp_help_page_slug for integration json * integration logic as collapse * UI improvements * WIP * Trying to debug vue devtools * WIP for integrations * getIntegrationHandler change namespace name * useForm for integration fields + validation structure * Integration Test case & apply validation rules * Apply useform changes to integration other files * validation rules for FormNotificationsMessageActions fields * Zapier integration as coming soon * Update FormCleaner * set default settings for confirmation integration * WIP * Finish validation for all integrations * Updated purify, added integration formatData * Fix testcase * Ran pint; working on integration errors * Handle integration events * command for Delete Old Integration Events * Display Past Events in Modal * on Integration event create with status error send email to form creator * Polish styling * Minor improvements * Finish badge and integration status * Fix tests and add an integration event test * Lint --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2024-03-28 18:14:30 +01:00
if (!$this->isPro($workspace)) {
$this->data = $this->removeProFeatures($this->data, true);
}
2022-09-20 21:59:52 +02:00
return $this;
}
/**
* Perform Cleanigns
2024-02-23 11:54:12 +01:00
*
2022-09-20 21:59:52 +02:00
* @param User|null $user
* @return $this|array
*/
public function performCleaning(Workspace $workspace): FormCleaner
{
if ($this->isTrialing($workspace)) {
$this->data = $this->removeNonTrialingFeatures($this->data, true);
}
Notification & Integrations refactoring (#346) * Integrations Refactoring - WIP * integrations list & edit - WIP * Fix integration store binding issue * integrations refactor - WIP * Form integration - WIP * Form integration Edit - WIP * Integration Refactor - Slack - WIP * Integration Refactor - Discord - WIP * Integration Refactor - Webhook - WIP * Integration Refactor - Send Submission Confirmation - WIP * Integration Refactor - Backend handler - WIP * Form Integration Status field * Integration Refactor - Backend SubmissionConfirmation - WIP * IntegrationMigration Command * skip confirmation email test case * Small refactoring * FormIntegration status active/inactive * formIntegrationData to integrationData * Rename file name with Integration suffix for integration realted files * Loader on form integrations * WIP * form integration test case * WIP * Added Integration card - working on refactoring * change location for IntegrationCard and update package file * Form Integration Create/Edit in single Modal * Remove integration extra pages * crisp_help_page_slug for integration json * integration logic as collapse * UI improvements * WIP * Trying to debug vue devtools * WIP for integrations * getIntegrationHandler change namespace name * useForm for integration fields + validation structure * Integration Test case & apply validation rules * Apply useform changes to integration other files * validation rules for FormNotificationsMessageActions fields * Zapier integration as coming soon * Update FormCleaner * set default settings for confirmation integration * WIP * Finish validation for all integrations * Updated purify, added integration formatData * Fix testcase * Ran pint; working on integration errors * Handle integration events * command for Delete Old Integration Events * Display Past Events in Modal * on Integration event create with status error send email to form creator * Polish styling * Minor improvements * Finish badge and integration status * Fix tests and add an integration event test * Lint --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2024-03-28 18:14:30 +01:00
if (!$this->isPro($workspace)) {
$this->data = $this->removeProFeatures($this->data);
}
2022-09-20 21:59:52 +02:00
return $this;
}
/**
* Clean all forms:
* - Escape html of custom text block
*/
private function commonCleaning(array $data)
{
foreach ($data['properties'] as &$property) {
if ($property['type'] == 'nf-text' && isset($property['content'])) {
$property['content'] = Purify::clean($property['content']);
}
}
return $data;
}
private function removeNonTrialingFeatures(array $data, $simulation = false)
{
$this->clean($data, $this->formNonTrialingDefaults);
return $data;
}
2022-09-20 21:59:52 +02:00
private function removeProFeatures(array $data, $simulation = false)
{
$this->cleanForm($data, $simulation);
$this->cleanProperties($data, $simulation);
return $data;
}
private function cleanForm(array &$data, $simulation = false): void
{
$this->clean($data, $this->formDefaults, $simulation);
}
private function cleanProperties(array &$data, $simulation = false): void
{
foreach ($data['properties'] as $key => &$property) {
/*
2022-09-20 21:59:52 +02:00
// Remove pro custom blocks
if (\Str::of($property['type'])->startsWith('nf-')) {
$this->cleanings[$property['name']][] = 'custom_block';
if (!$simulation) {
unset($data['properties'][$key]);
}
continue;
}
// Remove logic
if (($property['logic']['conditions'] ?? null) != null || ($property['logic']['actions'] ?? []) != []) {
$this->cleanings[$property['name']][] = 'logic';
if (!$simulation) {
unset($data['properties'][$key]['logic']);
}
}
*/
2022-09-20 21:59:52 +02:00
// Clean pro field options
$this->cleanField($property, $this->fieldDefaults, $simulation);
}
}
private function clean(array &$data, array $defaults, $simulation = false): void
{
foreach ($defaults as $key => $value) {
// Get value from form
$formVal = Arr::get($data, $key);
// Transform customkeys values
$formVal = $this->cleanCustomKeys($key, $formVal);
// Transform boolean values
2024-02-23 11:54:12 +01:00
$formVal = (($formVal === 0 || $formVal === '0') ? false : $formVal);
$formVal = (($formVal === 1 || $formVal === '1') ? true : $formVal);
Notification & Integrations refactoring (#346) * Integrations Refactoring - WIP * integrations list & edit - WIP * Fix integration store binding issue * integrations refactor - WIP * Form integration - WIP * Form integration Edit - WIP * Integration Refactor - Slack - WIP * Integration Refactor - Discord - WIP * Integration Refactor - Webhook - WIP * Integration Refactor - Send Submission Confirmation - WIP * Integration Refactor - Backend handler - WIP * Form Integration Status field * Integration Refactor - Backend SubmissionConfirmation - WIP * IntegrationMigration Command * skip confirmation email test case * Small refactoring * FormIntegration status active/inactive * formIntegrationData to integrationData * Rename file name with Integration suffix for integration realted files * Loader on form integrations * WIP * form integration test case * WIP * Added Integration card - working on refactoring * change location for IntegrationCard and update package file * Form Integration Create/Edit in single Modal * Remove integration extra pages * crisp_help_page_slug for integration json * integration logic as collapse * UI improvements * WIP * Trying to debug vue devtools * WIP for integrations * getIntegrationHandler change namespace name * useForm for integration fields + validation structure * Integration Test case & apply validation rules * Apply useform changes to integration other files * validation rules for FormNotificationsMessageActions fields * Zapier integration as coming soon * Update FormCleaner * set default settings for confirmation integration * WIP * Finish validation for all integrations * Updated purify, added integration formatData * Fix testcase * Ran pint; working on integration errors * Handle integration events * command for Delete Old Integration Events * Display Past Events in Modal * on Integration event create with status error send email to form creator * Polish styling * Minor improvements * Finish badge and integration status * Fix tests and add an integration event test * Lint --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2024-03-28 18:14:30 +01:00
if (!is_null($formVal) && $formVal !== $value) {
if (!isset($this->cleanings['form'])) {
$this->cleanings['form'] = [];
}
2022-09-20 21:59:52 +02:00
$this->cleanings['form'][] = $key;
// If not a simulation, do the cleaning
Notification & Integrations refactoring (#346) * Integrations Refactoring - WIP * integrations list & edit - WIP * Fix integration store binding issue * integrations refactor - WIP * Form integration - WIP * Form integration Edit - WIP * Integration Refactor - Slack - WIP * Integration Refactor - Discord - WIP * Integration Refactor - Webhook - WIP * Integration Refactor - Send Submission Confirmation - WIP * Integration Refactor - Backend handler - WIP * Form Integration Status field * Integration Refactor - Backend SubmissionConfirmation - WIP * IntegrationMigration Command * skip confirmation email test case * Small refactoring * FormIntegration status active/inactive * formIntegrationData to integrationData * Rename file name with Integration suffix for integration realted files * Loader on form integrations * WIP * form integration test case * WIP * Added Integration card - working on refactoring * change location for IntegrationCard and update package file * Form Integration Create/Edit in single Modal * Remove integration extra pages * crisp_help_page_slug for integration json * integration logic as collapse * UI improvements * WIP * Trying to debug vue devtools * WIP for integrations * getIntegrationHandler change namespace name * useForm for integration fields + validation structure * Integration Test case & apply validation rules * Apply useform changes to integration other files * validation rules for FormNotificationsMessageActions fields * Zapier integration as coming soon * Update FormCleaner * set default settings for confirmation integration * WIP * Finish validation for all integrations * Updated purify, added integration formatData * Fix testcase * Ran pint; working on integration errors * Handle integration events * command for Delete Old Integration Events * Display Past Events in Modal * on Integration event create with status error send email to form creator * Polish styling * Minor improvements * Finish badge and integration status * Fix tests and add an integration event test * Lint --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2024-03-28 18:14:30 +01:00
if (!$simulation) {
2022-09-20 21:59:52 +02:00
Arr::set($data, $key, $value);
}
}
}
}
private function cleanField(array &$data, array $defaults, $simulation = false): void
{
foreach ($defaults as $key => $value) {
if (isset($data[$key]) && Arr::get($data, $key) !== $value) {
$this->cleanings[$data['name']][] = $key;
Notification & Integrations refactoring (#346) * Integrations Refactoring - WIP * integrations list & edit - WIP * Fix integration store binding issue * integrations refactor - WIP * Form integration - WIP * Form integration Edit - WIP * Integration Refactor - Slack - WIP * Integration Refactor - Discord - WIP * Integration Refactor - Webhook - WIP * Integration Refactor - Send Submission Confirmation - WIP * Integration Refactor - Backend handler - WIP * Form Integration Status field * Integration Refactor - Backend SubmissionConfirmation - WIP * IntegrationMigration Command * skip confirmation email test case * Small refactoring * FormIntegration status active/inactive * formIntegrationData to integrationData * Rename file name with Integration suffix for integration realted files * Loader on form integrations * WIP * form integration test case * WIP * Added Integration card - working on refactoring * change location for IntegrationCard and update package file * Form Integration Create/Edit in single Modal * Remove integration extra pages * crisp_help_page_slug for integration json * integration logic as collapse * UI improvements * WIP * Trying to debug vue devtools * WIP for integrations * getIntegrationHandler change namespace name * useForm for integration fields + validation structure * Integration Test case & apply validation rules * Apply useform changes to integration other files * validation rules for FormNotificationsMessageActions fields * Zapier integration as coming soon * Update FormCleaner * set default settings for confirmation integration * WIP * Finish validation for all integrations * Updated purify, added integration formatData * Fix testcase * Ran pint; working on integration errors * Handle integration events * command for Delete Old Integration Events * Display Past Events in Modal * on Integration event create with status error send email to form creator * Polish styling * Minor improvements * Finish badge and integration status * Fix tests and add an integration event test * Lint --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2024-03-28 18:14:30 +01:00
if (!$simulation) {
2022-09-20 21:59:52 +02:00
Arr::set($data, $key, $value);
}
}
}
// Remove pro types columns
/*foreach (['files'] as $proType) {
2022-09-20 21:59:52 +02:00
if ($data['type'] == $proType && (!isset($data['hidden']) || !$data['hidden'])) {
$this->cleanings[$data['name']][] = $proType;
if (!$simulation) {
$data['hidden'] = true;
}
}
}*/
2022-09-20 21:59:52 +02:00
}
// Remove keys those have empty value
private function cleanCustomKeys($key, $formVal)
{
if (in_array($key, $this->customKeys) && $formVal !== null) {
$newVal = [];
foreach ($formVal as $k => $val) {
if ($val) {
$newVal[$k] = $val;
}
}
2024-02-23 11:54:12 +01:00
return $newVal;
}
return $formVal;
}
2022-09-20 21:59:52 +02:00
}