Lint PHP code psr-12, add GH action

This commit is contained in:
Julien Nahum
2024-02-23 11:54:12 +01:00
parent e85e4df7fe
commit 62971a2ef4
226 changed files with 2338 additions and 2144 deletions

View File

@@ -1,22 +1,23 @@
<?php
namespace App\Service\Forms;
use App\Http\Requests\UserFormRequest;
use App\Http\Resources\FormResource;
use App\Models\Forms\Form;
use App\Models\Workspace;
use App\Models\User;
use App\Models\Workspace;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Stevebauman\Purify\Facades\Purify;
use function collect;
class FormCleaner
{
/**
* All the performed cleanings
*
* @var bool
*/
private array $cleanings = [];
@@ -35,7 +36,7 @@ class FormCleaner
'discord_webhook_url' => null,
'editable_submissions' => false,
'custom_code' => null,
'seo_meta' => []
'seo_meta' => [],
];
private array $fieldDefaults = [
@@ -45,24 +46,23 @@ class FormCleaner
private array $cleaningMessages = [
// For form
'notifies' => "Email notification were disabled.",
'no_branding' => "OpenForm branding is not hidden.",
'webhook_url' => "Webhook disabled.",
'notifies' => 'Email notification were disabled.',
'no_branding' => 'OpenForm branding is not hidden.',
'webhook_url' => 'Webhook disabled.',
'database_fields_update' => 'Form submission will only create new records (no updates).',
'slack_webhook_url' => "Slack webhook disabled.",
'discord_webhook_url' => "Discord webhook disabled.",
'slack_webhook_url' => 'Slack webhook disabled.',
'discord_webhook_url' => 'Discord webhook disabled.',
'editable_submissions' => 'Users will not be able to edit their submissions.',
'custom_code' => 'Custom code was disabled',
'seo_meta' => 'Custom SEO was disabled',
// For fields
'file_upload' => "Link field is not a file upload.",
'file_upload' => 'Link field is not a file upload.',
'custom_block' => 'The custom block was removed.',
];
/**
* Returns form data after request ingestion
* @return array
*/
public function getData(): array
{
@@ -71,7 +71,6 @@ class FormCleaner
/**
* Returns true if at least one cleaning was done
* @return bool
*/
public function hasCleaned(): bool
{
@@ -89,6 +88,7 @@ class FormCleaner
return $this->cleaningMessages[$cleaning];
});
}
return $cleaningMsgs;
}
@@ -106,7 +106,7 @@ class FormCleaner
/**
* Create form cleaner instance from existing form
*/
public function processForm(Request $request, Form $form) : FormCleaner
public function processForm(Request $request, Form $form): FormCleaner
{
$data = (new FormResource($form))->toArray($request);
$this->data = $this->commonCleaning($data);
@@ -114,17 +114,19 @@ class FormCleaner
return $this;
}
private function isPro(Workspace $workspace) {
private function isPro(Workspace $workspace)
{
return $workspace->is_pro;
}
/**
* Dry run celanings
*
* @param User|null $user
*/
public function simulateCleaning(Workspace $workspace): FormCleaner
public function simulateCleaning(Workspace $workspace): FormCleaner
{
if (!$this->isPro($workspace)) {
if (! $this->isPro($workspace)) {
$this->data = $this->removeProFeatures($this->data, true);
}
@@ -133,12 +135,13 @@ class FormCleaner
/**
* Perform Cleanigns
*
* @param User|null $user
* @return $this|array
*/
public function performCleaning(Workspace $workspace): FormCleaner
{
if (!$this->isPro($workspace)) {
if (! $this->isPro($workspace)) {
$this->data = $this->removeProFeatures($this->data);
}
@@ -211,17 +214,17 @@ class FormCleaner
$formVal = $this->cleanCustomKeys($key, $formVal);
// Transform boolean values
$formVal = (($formVal === 0 || $formVal === "0") ? false : $formVal);
$formVal = (($formVal === 1 || $formVal === "1") ? true : $formVal);
$formVal = (($formVal === 0 || $formVal === '0') ? false : $formVal);
$formVal = (($formVal === 1 || $formVal === '1') ? true : $formVal);
if (!is_null($formVal) && $formVal !== $value) {
if (!isset($this->cleanings['form'])) {
if (! is_null($formVal) && $formVal !== $value) {
if (! isset($this->cleanings['form'])) {
$this->cleanings['form'] = [];
}
$this->cleanings['form'][] = $key;
// If not a simulation, do the cleaning
if (!$simulation) {
if (! $simulation) {
Arr::set($data, $key, $value);
}
}
@@ -233,7 +236,7 @@ class FormCleaner
foreach ($defaults as $key => $value) {
if (isset($data[$key]) && Arr::get($data, $key) !== $value) {
$this->cleanings[$data['name']][] = $key;
if (!$simulation) {
if (! $simulation) {
Arr::set($data, $key, $value);
}
}
@@ -260,10 +263,10 @@ class FormCleaner
$newVal[$k] = $val;
}
}
return $newVal;
}
return $formVal;
}
}

View File

@@ -2,88 +2,95 @@
namespace App\Service\Forms;
use Mockery\Matcher\Any;
use function PHPUnit\Framework\isEmpty;
class FormLogicConditionChecker
{
public function __construct(private ?array $conditions, private ?array $formData)
{
}
public static function conditionsMet(?array $conditions, array $formData): bool {
public static function conditionsMet(?array $conditions, array $formData): bool
{
return (new self($conditions, $formData))->conditionsAreMet($conditions, $formData);
}
private function conditionsAreMet(?array $conditions, array $formData): bool {
if (!$conditions) {
private function conditionsAreMet(?array $conditions, array $formData): bool
{
if (! $conditions) {
return false;
}
// If it's not a group, just a single condition
if (!isset($conditions['operatorIdentifier'])) {
if (! isset($conditions['operatorIdentifier'])) {
return $this->propertyConditionMet($conditions['value'], $formData[$conditions['value']['property_meta']['id']] ?? null);
}
if ($conditions['operatorIdentifier'] === 'and') {
$isvalid = true;
foreach($conditions['children'] as $childrenCondition){
if (!$this->conditionsMet($childrenCondition, $formData)) {
foreach ($conditions['children'] as $childrenCondition) {
if (! $this->conditionsMet($childrenCondition, $formData)) {
$isvalid = false;
break;
}
}
return $isvalid;
} else if ($conditions['operatorIdentifier'] === 'or') {
} elseif ($conditions['operatorIdentifier'] === 'or') {
$isvalid = false;
foreach($conditions['children'] as $childrenCondition){
foreach ($conditions['children'] as $childrenCondition) {
if ($this->conditionsMet($childrenCondition, $formData)) {
$isvalid = true;
break;
}
}
return $isvalid;
}
throw new \Exception('Unexcepted operatorIdentifier:'. $conditions['operatorIdentifier']);
throw new \Exception('Unexcepted operatorIdentifier:'.$conditions['operatorIdentifier']);
}
private function propertyConditionMet(array $propertyCondition, $value): bool {
private function propertyConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['property_meta']['type']) {
case 'text':
case 'url':
case 'email':
case 'phone_number':
return $this->textConditionMet($propertyCondition, $value);
return $this->textConditionMet($propertyCondition, $value);
case 'number':
return $this->numberConditionMet($propertyCondition, $value);
return $this->numberConditionMet($propertyCondition, $value);
case 'checkbox':
return $this->checkboxConditionMet($propertyCondition, $value);
return $this->checkboxConditionMet($propertyCondition, $value);
case 'select':
return $this->selectConditionMet($propertyCondition, $value);
return $this->selectConditionMet($propertyCondition, $value);
case 'date':
return $this->dateConditionMet($propertyCondition, $value);
return $this->dateConditionMet($propertyCondition, $value);
case 'multi_select':
return $this->multiSelectConditionMet($propertyCondition, $value);
return $this->multiSelectConditionMet($propertyCondition, $value);
case 'files':
return $this->filesConditionMet($propertyCondition, $value);
}
return false;
return $this->filesConditionMet($propertyCondition, $value);
}
return false;
}
private function checkEquals ($condition, $fieldValue): bool {
private function checkEquals($condition, $fieldValue): bool
{
return $condition['value'] === $fieldValue;
}
private function checkContains ($condition, $fieldValue): bool {
private function checkContains($condition, $fieldValue): bool
{
return ($fieldValue && is_array($fieldValue)) ? in_array($condition['value'], $fieldValue) : false;
}
private function checkListContains ($condition, $fieldValue): bool {
if (is_null($fieldValue)) return false;
private function checkListContains($condition, $fieldValue): bool
{
if (is_null($fieldValue)) {
return false;
}
if (!is_array($fieldValue)) {
if (! is_array($fieldValue)) {
return $this->checkEquals($condition, $fieldValue);
}
@@ -94,252 +101,305 @@ class FormLogicConditionChecker
}
}
private function checkStartsWith ($condition, $fieldValue): bool {
private function checkStartsWith($condition, $fieldValue): bool
{
return str_starts_with($fieldValue, $condition['value']);
}
private function checkEndsWith ($condition, $fieldValue): bool {
private function checkEndsWith($condition, $fieldValue): bool
{
return str_ends_with($fieldValue, $condition['value']);
}
private function checkIsEmpty ($condition, $fieldValue): bool {
if(is_array($fieldValue)){
private function checkIsEmpty($condition, $fieldValue): bool
{
if (is_array($fieldValue)) {
return count($fieldValue) === 0;
}
return $fieldValue == '' || $fieldValue == null || !$fieldValue;
return $fieldValue == '' || $fieldValue == null || ! $fieldValue;
}
private function checkGreaterThan ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && (float)$fieldValue > (float)$condition['value']);
private function checkGreaterThan($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && (float) $fieldValue > (float) $condition['value'];
}
private function checkGreaterThanEqual ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && (float)$fieldValue >= (float)$condition['value']);
private function checkGreaterThanEqual($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && (float) $fieldValue >= (float) $condition['value'];
}
private function checkLessThan ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && (float)$fieldValue < (float)$condition['value']);
private function checkLessThan($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && (float) $fieldValue < (float) $condition['value'];
}
private function checkLessThanEqual ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && (float)$fieldValue <= (float)$condition['value']);
private function checkLessThanEqual($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && (float) $fieldValue <= (float) $condition['value'];
}
private function checkBefore ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && $fieldValue > $condition['value']);
private function checkBefore($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && $fieldValue > $condition['value'];
}
private function checkAfter ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && $fieldValue < $condition['value']);
private function checkAfter($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && $fieldValue < $condition['value'];
}
private function checkOnOrBefore ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && $fieldValue >= $condition['value']);
private function checkOnOrBefore($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && $fieldValue >= $condition['value'];
}
private function checkOnOrAfter ($condition, $fieldValue): bool {
return ($condition['value'] && $fieldValue && $fieldValue <= $condition['value']);
private function checkOnOrAfter($condition, $fieldValue): bool
{
return $condition['value'] && $fieldValue && $fieldValue <= $condition['value'];
}
private function checkPastWeek ($condition, $fieldValue): bool {
if(!$fieldValue) return false;
private function checkPastWeek($condition, $fieldValue): bool
{
if (! $fieldValue) {
return false;
}
$fieldDate = date('Y-m-d', strtotime($fieldValue));
return ($fieldDate <= now()->toDateString() && $fieldDate >= now()->subDays(7)->toDateString());
return $fieldDate <= now()->toDateString() && $fieldDate >= now()->subDays(7)->toDateString();
}
private function checkPastMonth ($condition, $fieldValue): bool {
if(!$fieldValue) return false;
private function checkPastMonth($condition, $fieldValue): bool
{
if (! $fieldValue) {
return false;
}
$fieldDate = date('Y-m-d', strtotime($fieldValue));
return ($fieldDate <= now()->toDateString() && $fieldDate >= now()->subMonths(1)->toDateString());
return $fieldDate <= now()->toDateString() && $fieldDate >= now()->subMonths(1)->toDateString();
}
private function checkPastYear ($condition, $fieldValue): bool {
if(!$fieldValue) return false;
private function checkPastYear($condition, $fieldValue): bool
{
if (! $fieldValue) {
return false;
}
$fieldDate = date('Y-m-d', strtotime($fieldValue));
return ($fieldDate <= now()->toDateString() && $fieldDate >= now()->subYears(1)->toDateString());
return $fieldDate <= now()->toDateString() && $fieldDate >= now()->subYears(1)->toDateString();
}
private function checkNextWeek ($condition, $fieldValue): bool {
if(!$fieldValue) return false;
private function checkNextWeek($condition, $fieldValue): bool
{
if (! $fieldValue) {
return false;
}
$fieldDate = date('Y-m-d', strtotime($fieldValue));
return ($fieldDate >= now()->toDateString() && $fieldDate <= now()->addDays(7)->toDateString());
return $fieldDate >= now()->toDateString() && $fieldDate <= now()->addDays(7)->toDateString();
}
private function checkNextMonth ($condition, $fieldValue): bool {
if(!$fieldValue) return false;
private function checkNextMonth($condition, $fieldValue): bool
{
if (! $fieldValue) {
return false;
}
$fieldDate = date('Y-m-d', strtotime($fieldValue));
return ($fieldDate >= now()->toDateString() && $fieldDate <= now()->addMonths(1)->toDateString());
return $fieldDate >= now()->toDateString() && $fieldDate <= now()->addMonths(1)->toDateString();
}
private function checkNextYear ($condition, $fieldValue): bool {
if(!$fieldValue) return false;
private function checkNextYear($condition, $fieldValue): bool
{
if (! $fieldValue) {
return false;
}
$fieldDate = date('Y-m-d', strtotime($fieldValue));
return ($fieldDate >= now()->toDateString() && $fieldDate <= now()->addYears(1)->toDateString());
return $fieldDate >= now()->toDateString() && $fieldDate <= now()->addYears(1)->toDateString();
}
private function checkLength ($condition, $fieldValue, $operator = '==='): bool {
if(!$fieldValue || strlen($fieldValue) === 0) return false;
private function checkLength($condition, $fieldValue, $operator = '==='): bool
{
if (! $fieldValue || strlen($fieldValue) === 0) {
return false;
}
switch ($operator) {
case '===':
return strlen($fieldValue) === (int)$condition['value'];
case '!==':
return strlen($fieldValue) !== (int)$condition['value'];
case '>':
return strlen($fieldValue) > (int)$condition['value'];
case '>=':
return strlen($fieldValue) >= (int)$condition['value'];
case '<':
return strlen($fieldValue) < (int)$condition['value'];
case '<=':
return strlen($fieldValue) <= (int)$condition['value'];
case '===':
return strlen($fieldValue) === (int) $condition['value'];
case '!==':
return strlen($fieldValue) !== (int) $condition['value'];
case '>':
return strlen($fieldValue) > (int) $condition['value'];
case '>=':
return strlen($fieldValue) >= (int) $condition['value'];
case '<':
return strlen($fieldValue) < (int) $condition['value'];
case '<=':
return strlen($fieldValue) <= (int) $condition['value'];
}
return false;
}
private function textConditionMet (array $propertyCondition, $value): bool {
private function textConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return !$this->checkEquals($propertyCondition, $value);
case 'contains':
return $this->checkContains($propertyCondition, $value);
case 'does_not_contain':
return !$this->checkContains($propertyCondition, $value);
case 'starts_with':
return $this->checkStartsWith($propertyCondition, $value);
case 'ends_with':
return $this->checkEndsWith($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return !$this->checkIsEmpty($propertyCondition, $value);
case 'content_length_equals':
return $this->checkLength($propertyCondition, $value, '===');
case 'content_length_does_not_equal':
return $this->checkLength($propertyCondition, $value, '!==');
case 'content_length_greater_than':
return $this->checkLength($propertyCondition, $value, '>');
case 'content_length_greater_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '>=');
case 'content_length_less_than':
return $this->checkLength($propertyCondition, $value, '<');
case 'content_length_less_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '<=');
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return ! $this->checkEquals($propertyCondition, $value);
case 'contains':
return $this->checkContains($propertyCondition, $value);
case 'does_not_contain':
return ! $this->checkContains($propertyCondition, $value);
case 'starts_with':
return $this->checkStartsWith($propertyCondition, $value);
case 'ends_with':
return $this->checkEndsWith($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return ! $this->checkIsEmpty($propertyCondition, $value);
case 'content_length_equals':
return $this->checkLength($propertyCondition, $value, '===');
case 'content_length_does_not_equal':
return $this->checkLength($propertyCondition, $value, '!==');
case 'content_length_greater_than':
return $this->checkLength($propertyCondition, $value, '>');
case 'content_length_greater_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '>=');
case 'content_length_less_than':
return $this->checkLength($propertyCondition, $value, '<');
case 'content_length_less_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '<=');
}
return false;
}
private function numberConditionMet (array $propertyCondition, $value): bool {
private function numberConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return !$this->checkEquals($propertyCondition, $value);
case 'greater_than':
return $this->checkGreaterThan($propertyCondition, $value);
case 'less_than':
return $this->checkLessThan($propertyCondition, $value);
case 'greater_than_or_equal_to':
return $this->checkGreaterThanEqual($propertyCondition, $value);
case 'less_than_or_equal_to':
return $this->checkLessThanEqual($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return !$this->checkIsEmpty($propertyCondition, $value);
case 'content_length_equals':
return $this->checkLength($propertyCondition, $value, '===');
case 'content_length_does_not_equal':
return $this->checkLength($propertyCondition, $value, '!==');
case 'content_length_greater_than':
return $this->checkLength($propertyCondition, $value, '>');
case 'content_length_greater_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '>=');
case 'content_length_less_than':
return $this->checkLength($propertyCondition, $value, '<');
case 'content_length_less_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '<=');
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return ! $this->checkEquals($propertyCondition, $value);
case 'greater_than':
return $this->checkGreaterThan($propertyCondition, $value);
case 'less_than':
return $this->checkLessThan($propertyCondition, $value);
case 'greater_than_or_equal_to':
return $this->checkGreaterThanEqual($propertyCondition, $value);
case 'less_than_or_equal_to':
return $this->checkLessThanEqual($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return ! $this->checkIsEmpty($propertyCondition, $value);
case 'content_length_equals':
return $this->checkLength($propertyCondition, $value, '===');
case 'content_length_does_not_equal':
return $this->checkLength($propertyCondition, $value, '!==');
case 'content_length_greater_than':
return $this->checkLength($propertyCondition, $value, '>');
case 'content_length_greater_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '>=');
case 'content_length_less_than':
return $this->checkLength($propertyCondition, $value, '<');
case 'content_length_less_than_or_equal_to':
return $this->checkLength($propertyCondition, $value, '<=');
}
return false;
}
private function checkboxConditionMet (array $propertyCondition, $value): bool {
private function checkboxConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return !$this->checkEquals($propertyCondition, $value);
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return ! $this->checkEquals($propertyCondition, $value);
}
return false;
}
private function selectConditionMet (array $propertyCondition, $value): bool {
private function selectConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return !$this->checkEquals($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return !$this->checkIsEmpty($propertyCondition, $value);
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'does_not_equal':
return ! $this->checkEquals($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return ! $this->checkIsEmpty($propertyCondition, $value);
}
return false;
}
private function dateConditionMet (array $propertyCondition, $value): bool {
private function dateConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'before':
return $this->checkBefore($propertyCondition, $value);
case 'after':
return $this->checkAfter($propertyCondition, $value);
case 'on_or_before':
return $this->checkOnOrBefore($propertyCondition, $value);
case 'on_or_after':
return $this->checkOnOrAfter($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'past_week':
return $this->checkPastWeek($propertyCondition, $value);
case 'past_month':
return $this->checkPastMonth($propertyCondition, $value);
case 'past_year':
return $this->checkPastYear($propertyCondition, $value);
case 'next_week':
return $this->checkNextWeek($propertyCondition, $value);
case 'next_month':
return $this->checkNextMonth($propertyCondition, $value);
case 'next_year':
return $this->checkNextYear($propertyCondition, $value);
case 'equals':
return $this->checkEquals($propertyCondition, $value);
case 'before':
return $this->checkBefore($propertyCondition, $value);
case 'after':
return $this->checkAfter($propertyCondition, $value);
case 'on_or_before':
return $this->checkOnOrBefore($propertyCondition, $value);
case 'on_or_after':
return $this->checkOnOrAfter($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'past_week':
return $this->checkPastWeek($propertyCondition, $value);
case 'past_month':
return $this->checkPastMonth($propertyCondition, $value);
case 'past_year':
return $this->checkPastYear($propertyCondition, $value);
case 'next_week':
return $this->checkNextWeek($propertyCondition, $value);
case 'next_month':
return $this->checkNextMonth($propertyCondition, $value);
case 'next_year':
return $this->checkNextYear($propertyCondition, $value);
}
return false;
}
private function multiSelectConditionMet (array $propertyCondition, $value): bool {
private function multiSelectConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'contains':
return $this->checkListContains($propertyCondition, $value);
case 'does_not_contain':
return !$this->checkListContains($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return !$this->checkIsEmpty($propertyCondition, $value);
case 'contains':
return $this->checkListContains($propertyCondition, $value);
case 'does_not_contain':
return ! $this->checkListContains($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return ! $this->checkIsEmpty($propertyCondition, $value);
}
return false;
}
private function filesConditionMet (array $propertyCondition, $value): bool {
private function filesConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return !$this->checkIsEmpty($propertyCondition, $value);
case 'is_empty':
return $this->checkIsEmpty($propertyCondition, $value);
case 'is_not_empty':
return ! $this->checkIsEmpty($propertyCondition, $value);
}
return false;
}
}

View File

@@ -2,13 +2,12 @@
namespace App\Service\Forms;
use App\Service\Forms\FormLogicConditionChecker;
class FormLogicPropertyResolver
{
private $property = [];
private $formData = [];
private $logic = false;
public function __construct(private array $prop, private array $values)
@@ -30,18 +29,18 @@ class FormLogicPropertyResolver
public function shouldBeRequired(): bool
{
if(!isset($this->property['required'])){
if (! isset($this->property['required'])) {
return false;
}
if (!$this->logic) {
if (! $this->logic) {
return $this->property['required'];
}
$conditionsMet = FormLogicConditionChecker::conditionsMet($this->logic['conditions'], $this->formData);
if ($conditionsMet && $this->property['required'] && count($this->logic['actions']) > 0 && (in_array('make-it-optional', $this->logic['actions']) || in_array('hide-block', $this->logic['actions']))) {
return false;
} else if ($conditionsMet && !$this->property['required'] && count($this->logic['actions']) > 0 && in_array('require-answer', $this->logic['actions'])) {
} elseif ($conditionsMet && ! $this->property['required'] && count($this->logic['actions']) > 0 && in_array('require-answer', $this->logic['actions'])) {
return true;
} else {
return $this->property['required'];
@@ -54,14 +53,14 @@ class FormLogicPropertyResolver
return false;
}
if (!$this->logic) {
if (! $this->logic) {
return $this->property['hidden'];
}
$conditionsMet = FormLogicConditionChecker::conditionsMet($this->logic['conditions'], $this->formData);
if ($conditionsMet && $this->property['hidden'] && count($this->logic['actions']) > 0 && in_array('show-block', $this->logic['actions'])) {
return false;
} elseif ($conditionsMet && !$this->property['hidden'] && count($this->logic['actions']) > 0 && in_array('hide-block', $this->logic['actions'])) {
} elseif ($conditionsMet && ! $this->property['hidden'] && count($this->logic['actions']) > 0 && in_array('hide-block', $this->logic['actions'])) {
return true;
} else {
return $this->property['hidden'];

View File

@@ -1,24 +1,22 @@
<?php
namespace App\Service\Forms;
use App\Models\Forms\Form;
use App\Service\WorkspaceHelper;
use Carbon\Carbon;
class FormSubmissionFormatter
{
/**
* If true, creates html <a> links for emails and urls
*
* @var bool
*/
private $createLinks = false;
/**
* If true, serialize arrays
*
* @var bool
*/
private $outputStringsOnly = false;
@@ -38,42 +36,48 @@ class FormSubmissionFormatter
public function __construct(private Form $form, private array $formData)
{
$this->initIdFormData();
$this->initIdFormData();
}
public function createLinks()
{
$this->createLinks = true;
return $this;
}
public function showHiddenFields()
{
$this->showHiddenFields = true;
return $this;
}
public function outputStringsOnly()
{
$this->outputStringsOnly = true;
return $this;
}
public function setEmptyForNoValue()
{
$this->setEmptyForNoValue = true;
return $this;
}
public function showRemovedFields()
{
$this->showRemovedFields = true;
return $this;
}
public function useSignedUrlForFiles()
{
$this->useSignedUrlForFiles = true;
return $this;
}
@@ -90,40 +94,41 @@ class FormSubmissionFormatter
$removeFields = collect($this->form->removed_properties)->map(function ($field) {
return [
...$field,
'removed' => true
'removed' => true,
];
});
if ($this->showRemovedFields) {
$fields = $fields->merge($removeFields);
}
$fields = $fields->filter(function ($field) {
return !in_array($field['type'],['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image']);
return ! in_array($field['type'], ['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image']);
})->values();
$returnArray = [];
foreach ($fields as $field) {
if (in_array($field['id'],['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image'])) {
if (in_array($field['id'], ['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image'])) {
continue;
}
if($field['removed'] ?? false) {
$field['name'] = $field['name']." (deleted)";
if ($field['removed'] ?? false) {
$field['name'] = $field['name'].' (deleted)';
}
// Add ID to avoid name clashes
$field['name'] = $field['name'].' ('.\Str::of($field['id']).')';
// If not present skip
if (!isset($data[$field['id']])) {
if (! isset($data[$field['id']])) {
if ($this->setEmptyForNoValue) {
$returnArray[$field['name']] = '';
}
continue;
}
// If hide hidden fields
if (!$this->showHiddenFields) {
if (! $this->showHiddenFields) {
if (FormLogicPropertyResolver::isHidden($field, $this->idFormData ?? [])) {
continue;
}
@@ -143,7 +148,8 @@ class FormSubmissionFormatter
} elseif ($field['type'] == 'files') {
if ($this->outputStringsOnly) {
$formId = $this->form->id;
$returnArray[$field['name']] = implode(', ',
$returnArray[$field['name']] = implode(
', ',
collect($data[$field['id']])->map(function ($file) use ($formId) {
return $this->getFileUrl($formId, $file);
})->toArray()
@@ -151,11 +157,11 @@ class FormSubmissionFormatter
} else {
$formId = $this->form->id;
$returnArray[$field['name']] = collect($data[$field['id']])->map(function ($file) use ($formId) {
return [
'file_url' => $this->getFileUrl($formId, $file),
'file_name' => $file,
];
});
return [
'file_url' => $this->getFileUrl($formId, $file),
'file_name' => $file,
];
});
}
} else {
if (is_array($data[$field['id']])) {
@@ -164,6 +170,7 @@ class FormSubmissionFormatter
$returnArray[$field['name']] = $data[$field['id']];
}
}
return $returnArray;
}
@@ -177,12 +184,12 @@ class FormSubmissionFormatter
$fields = $this->form->properties;
$transformedFields = [];
foreach ($fields as $field) {
if (!isset($field['id']) || !isset($data[$field['id']])) {
if (! isset($field['id']) || ! isset($data[$field['id']])) {
continue;
}
// If hide hidden fields
if (!$this->showHiddenFields) {
if (! $this->showHiddenFields) {
if (FormLogicPropertyResolver::isHidden($field, $this->idFormData)) {
continue;
}
@@ -211,17 +218,19 @@ class FormSubmissionFormatter
} elseif ($field['type'] == 'files') {
if ($this->outputStringsOnly) {
$formId = $this->form->id;
$field['value'] = implode(', ',
$field['value'] = implode(
', ',
collect($data[$field['id']])->map(function ($file) use ($formId) {
return $this->getFileUrl($formId, $file);
return $this->getFileUrl($formId, $file);
})->toArray()
);
$field['email_data'] = collect($data[$field['id']])->map(function ($file) use ($formId) {
$splitText = explode('.', $file);
return [
"unsigned_url" => route('open.forms.submissions.file', [$formId, $file]),
"signed_url" => $this->getFileUrl($formId, $file),
"label" => \Str::limit($file, 20, '[...].'.end($splitText))
'unsigned_url' => route('open.forms.submissions.file', [$formId, $file]),
'signed_url' => $this->getFileUrl($formId, $file),
'label' => \Str::limit($file, 20, '[...].'.end($splitText)),
];
})->toArray();
} else {
@@ -243,10 +252,12 @@ class FormSubmissionFormatter
}
$transformedFields[] = $field;
}
return $transformedFields;
}
private function initIdFormData() {
private function initIdFormData()
{
$formProperties = collect($this->form->properties);
foreach ($this->formData as $key => $value) {
$property = $formProperties->first(function ($item) use ($key) {
@@ -262,7 +273,7 @@ class FormSubmissionFormatter
{
return $this->useSignedUrlForFiles ? \URL::signedRoute(
'open.forms.submissions.file',
[$formId, $file]) : route('open.forms.submissions.file', [$formId, $file]);
[$formId, $file]
) : route('open.forms.submissions.file', [$formId, $file]);
}
}

View File

@@ -4,8 +4,6 @@ namespace App\Service\Forms\Webhooks;
use App\Models\Forms\Form;
use App\Service\Forms\FormSubmissionFormatter;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Str;
use Spatie\WebhookServer\WebhookCall;
use Vinkla\Hashids\Facades\Hashids;
@@ -21,14 +19,12 @@ abstract class AbstractWebhookHandler
/**
* Default webhook payload. Can be changed in child classes.
* @return array
*/
protected function getWebhookData(): array
{
$formatter = (new FormSubmissionFormatter($this->form, $this->data))
->useSignedUrlForFiles()
->showHiddenFields()
;
->useSignedUrlForFiles()
->showHiddenFields();
$formattedData = [];
foreach ($formatter->getFieldsWithValue() as $field) {
@@ -41,7 +37,7 @@ abstract class AbstractWebhookHandler
'submission' => $formattedData,
];
if ($this->form->is_pro && $this->form->editable_submissions) {
$data['edit_link'] = $this->form->share_url . '?submission_id=' . Hashids::encode($this->data['submission_id']);
$data['edit_link'] = $this->form->share_url.'?submission_id='.Hashids::encode($this->data['submission_id']);
}
return $data;
@@ -51,8 +47,10 @@ abstract class AbstractWebhookHandler
public function handle()
{
if (!$this->shouldRun()) return;
if (! $this->shouldRun()) {
return;
}
WebhookCall::create()
// Add context on error, used to notify form owner
->meta([

View File

@@ -3,12 +3,11 @@
namespace App\Service\Forms\Webhooks;
use App\Service\Forms\FormSubmissionFormatter;
use Vinkla\Hashids\Facades\Hashids;
use Illuminate\Support\Arr;
use Vinkla\Hashids\Facades\Hashids;
class DiscordHandler extends AbstractWebhookHandler
{
protected function getProviderName(): string
{
return 'Discord';
@@ -21,66 +20,66 @@ class DiscordHandler extends AbstractWebhookHandler
protected function getWebhookData(): array
{
$settings = (array) Arr::get((array)$this->form->notification_settings, 'discord', []);
$settings = (array) Arr::get((array) $this->form->notification_settings, 'discord', []);
$externalLinks = [];
if(Arr::get($settings, 'link_open_form', true)){
$externalLinks[] = '[**🔗 Open Form**](' . $this->form->share_url . ')';
if (Arr::get($settings, 'link_open_form', true)) {
$externalLinks[] = '[**🔗 Open Form**]('.$this->form->share_url.')';
}
if(Arr::get($settings, 'link_edit_form', true)){
$editFormURL = front_url('forms/' . $this->form->slug . '/show');
$externalLinks[] = '[**✍️ Edit Form**](' . $editFormURL . ')';
if (Arr::get($settings, 'link_edit_form', true)) {
$editFormURL = front_url('forms/'.$this->form->slug.'/show');
$externalLinks[] = '[**✍️ Edit Form**]('.$editFormURL.')';
}
if (Arr::get($settings, 'link_edit_submission', true) && $this->form->editable_submissions) {
$submissionId = Hashids::encode($this->data['submission_id']);
$externalLinks[] = '[**✍️ ' . $this->form->editable_submissions_button_text . '**](' . $this->form->share_url . '?submission_id=' . $submissionId . ')';
$externalLinks[] = '[**✍️ '.$this->form->editable_submissions_button_text.'**]('.$this->form->share_url.'?submission_id='.$submissionId.')';
}
$color = hexdec(str_replace('#', '', $this->form->color));
$blocks = [];
if(Arr::get($settings, 'include_submission_data', true)){
$submissionString = "";
if (Arr::get($settings, 'include_submission_data', true)) {
$submissionString = '';
$formatter = (new FormSubmissionFormatter($this->form, $this->data))->outputStringsOnly();
foreach ($formatter->getFieldsWithValue() as $field) {
$tmpVal = is_array($field['value']) ? implode(",", $field['value']) : $field['value'];
$submissionString .= "**" . ucfirst($field['name']) . "**: " . $tmpVal . "\n";
$tmpVal = is_array($field['value']) ? implode(',', $field['value']) : $field['value'];
$submissionString .= '**'.ucfirst($field['name']).'**: '.$tmpVal."\n";
}
$blocks[] = [
"type" => "rich",
"color" => $color,
"description" => $submissionString
'type' => 'rich',
'color' => $color,
'description' => $submissionString,
];
}
if(Arr::get($settings, 'views_submissions_count', true)){
$countString = '**👀 Views**: ' . (string)$this->form->views_count . " \n";
$countString .= '**🖊️ Submissions**: ' . (string)$this->form->submissions_count;
if (Arr::get($settings, 'views_submissions_count', true)) {
$countString = '**👀 Views**: '.(string) $this->form->views_count." \n";
$countString .= '**🖊️ Submissions**: '.(string) $this->form->submissions_count;
$blocks[] = [
"type" => "rich",
"color" => $color,
"description" => $countString
'type' => 'rich',
'color' => $color,
'description' => $countString,
];
}
if(count($externalLinks) > 0){
if (count($externalLinks) > 0) {
$blocks[] = [
"type" => "rich",
"color" => $color,
"description" => implode(' - ', $externalLinks)
'type' => 'rich',
'color' => $color,
'description' => implode(' - ', $externalLinks),
];
}
return [
'content' => 'New submission for your form **' . $this->form->title . '**',
'content' => 'New submission for your form **'.$this->form->title.'**',
'tts' => false,
'username' => config('app.name'),
'avatar_url' => asset('img/logo.png'),
'embeds' => $blocks
'embeds' => $blocks,
];
}
protected function shouldRun(): bool
{
return !is_null($this->getWebhookUrl())
return ! is_null($this->getWebhookUrl())
&& str_contains($this->getWebhookUrl(), 'https://discord.com/api/webhooks')
&& $this->form->is_pro;
}

View File

@@ -2,9 +2,6 @@
namespace App\Service\Forms\Webhooks;
use App\Service\Forms\FormSubmissionFormatter;
use Illuminate\Support\Str;
class SimpleWebhookHandler extends AbstractWebhookHandler
{
protected function getProviderName(): string
@@ -19,6 +16,6 @@ class SimpleWebhookHandler extends AbstractWebhookHandler
protected function shouldRun(): bool
{
return !is_null($this->getWebhookUrl()) && $this->form->is_pro;
return ! is_null($this->getWebhookUrl()) && $this->form->is_pro;
}
}

View File

@@ -3,12 +3,11 @@
namespace App\Service\Forms\Webhooks;
use App\Service\Forms\FormSubmissionFormatter;
use Vinkla\Hashids\Facades\Hashids;
use Illuminate\Support\Arr;
use Vinkla\Hashids\Facades\Hashids;
class SlackHandler extends AbstractWebhookHandler
{
protected function getProviderName(): string
{
return 'Slack';
@@ -21,18 +20,18 @@ class SlackHandler extends AbstractWebhookHandler
protected function getWebhookData(): array
{
$settings = (array) Arr::get((array)$this->form->notification_settings, 'slack', []);
$settings = (array) Arr::get((array) $this->form->notification_settings, 'slack', []);
$externalLinks = [];
if(Arr::get($settings, 'link_open_form', true)){
$externalLinks[] = '*<' . $this->form->share_url . '|🔗 Open Form>*';
if (Arr::get($settings, 'link_open_form', true)) {
$externalLinks[] = '*<'.$this->form->share_url.'|🔗 Open Form>*';
}
if(Arr::get($settings, 'link_edit_form', true)){
$editFormURL = front_url('forms/' . $this->form->slug . '/show');
$externalLinks[] = '*<' . $editFormURL . '|✍️ Edit Form>*';
if (Arr::get($settings, 'link_edit_form', true)) {
$editFormURL = front_url('forms/'.$this->form->slug.'/show');
$externalLinks[] = '*<'.$editFormURL.'|✍️ Edit Form>*';
}
if (Arr::get($settings, 'link_edit_submission', true) && $this->form->editable_submissions) {
$submissionId = Hashids::encode($this->data['submission_id']);
$externalLinks[] = '*<' . $this->form->share_url . '?submission_id=' . $submissionId . '|✍️ ' . $this->form->editable_submissions_button_text . '>*';
$externalLinks[] = '*<'.$this->form->share_url.'?submission_id='.$submissionId.'|✍️ '.$this->form->editable_submissions_button_text.'>*';
}
$blocks = [
@@ -40,57 +39,57 @@ class SlackHandler extends AbstractWebhookHandler
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => 'New submission for your form *' . $this->form->title . '*',
]
]
'text' => 'New submission for your form *'.$this->form->title.'*',
],
],
];
if(Arr::get($settings, 'include_submission_data', true)){
if (Arr::get($settings, 'include_submission_data', true)) {
$submissionString = '';
$formatter = (new FormSubmissionFormatter($this->form, $this->data))->outputStringsOnly();
foreach ($formatter->getFieldsWithValue() as $field) {
$tmpVal = is_array($field['value']) ? implode(',', $field['value']) : $field['value'];
$submissionString .= '>*' . ucfirst($field['name']) . '*: ' . $tmpVal . " \n";
$submissionString .= '>*'.ucfirst($field['name']).'*: '.$tmpVal." \n";
}
$blocks[] = [
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => $submissionString,
]
],
];
}
if(Arr::get($settings, 'views_submissions_count', true)){
$countString = '*👀 Views*: ' . (string)$this->form->views_count . " \n";
$countString .= '*🖊️ Submissions*: ' . (string)$this->form->submissions_count;
if (Arr::get($settings, 'views_submissions_count', true)) {
$countString = '*👀 Views*: '.(string) $this->form->views_count." \n";
$countString .= '*🖊️ Submissions*: '.(string) $this->form->submissions_count;
$blocks[] = [
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => $countString,
]
],
];
}
if(count($externalLinks) > 0){
if (count($externalLinks) > 0) {
$blocks[] = [
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => implode(' ', $externalLinks),
]
],
];
}
return [
'blocks' => $blocks
'blocks' => $blocks,
];
}
protected function shouldRun(): bool
{
return !is_null($this->getWebhookUrl())
return ! is_null($this->getWebhookUrl())
&& str_contains($this->getWebhookUrl(), 'https://hooks.slack.com/')
&& $this->form->is_pro;
}

View File

@@ -6,10 +6,13 @@ use App\Models\Forms\Form;
class WebhookHandlerProvider
{
const SLACK_PROVIDER = 'slack';
const DISCORD_PROVIDER = 'discord';
const SIMPLE_WEBHOOK_PROVIDER = 'webhook';
const ZAPIER_PROVIDER = 'zapier';
public const SLACK_PROVIDER = 'slack';
public const DISCORD_PROVIDER = 'discord';
public const SIMPLE_WEBHOOK_PROVIDER = 'webhook';
public const ZAPIER_PROVIDER = 'zapier';
public static function getProvider(Form $form, array $data, string $provider, ?string $webhookUrl = null)
{
@@ -24,6 +27,7 @@ class WebhookHandlerProvider
if (is_null($webhookUrl)) {
throw new \Exception('Zapier webhook url is required');
}
return new ZapierHandler($form, $data, $webhookUrl);
default:
throw new \Exception('Unknown webhook provider');

View File

@@ -22,6 +22,6 @@ class ZapierHandler extends AbstractWebhookHandler
protected function shouldRun(): bool
{
return !is_null($this->getWebhookUrl());
return ! is_null($this->getWebhookUrl());
}
}