Lint PHP code psr-12, add GH action
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -22,6 +22,6 @@ class ZapierHandler extends AbstractWebhookHandler
|
||||
|
||||
protected function shouldRun(): bool
|
||||
{
|
||||
return !is_null($this->getWebhookUrl());
|
||||
return ! is_null($this->getWebhookUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Service\HtmlPurifier;
|
||||
|
||||
|
||||
use HTMLPurifier_Config;
|
||||
use HTMLPurifier_Context;
|
||||
use HTMLPurifier_URI;
|
||||
|
||||
class HTMLPurifier_URIScheme_notion extends \HTMLPurifier_URIScheme
|
||||
{
|
||||
public $browsable = true;
|
||||
|
||||
public $may_omit_host = true;
|
||||
|
||||
public function doValidate(&$uri, $config, $context)
|
||||
|
||||
@@ -14,15 +14,20 @@ use OpenAI\Exceptions\ErrorException;
|
||||
*/
|
||||
class GptCompleter
|
||||
{
|
||||
const AI_MODEL = 'gpt-4-turbo-preview';
|
||||
public const AI_MODEL = 'gpt-4-turbo-preview';
|
||||
|
||||
protected Client $openAi;
|
||||
|
||||
protected mixed $result;
|
||||
|
||||
protected array $completionInput;
|
||||
|
||||
protected ?string $systemMessage;
|
||||
|
||||
protected bool $expectsJson = false;
|
||||
|
||||
protected int $tokenUsed = 0;
|
||||
|
||||
protected bool $useStreaming = false;
|
||||
|
||||
public function __construct(string $apiKey, protected int $retries = 2, protected string $model = self::AI_MODEL)
|
||||
@@ -33,36 +38,41 @@ class GptCompleter
|
||||
public function setAiModel(string $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSystemMessage(string $systemMessage): self
|
||||
{
|
||||
$this->systemMessage = $systemMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useStreaming(): self
|
||||
{
|
||||
$this->useStreaming = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function expectsJson(): self
|
||||
{
|
||||
$this->expectsJson = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function doesNotExpectJson(): self
|
||||
{
|
||||
$this->expectsJson = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function completeChat(array $messages, int $maxTokens = 4096, float $temperature = 0.81, ?bool $exceptJson = null): self
|
||||
{
|
||||
if (!is_null($exceptJson)) {
|
||||
if (! is_null($exceptJson)) {
|
||||
$this->expectsJson = $exceptJson;
|
||||
}
|
||||
$this->computeChatCompletion($messages, $maxTokens, $temperature)
|
||||
@@ -89,18 +99,19 @@ class GptCompleter
|
||||
$payload = Str::of($this->result)->trim();
|
||||
if ($payload->contains('```json')) {
|
||||
$payload = $payload->after('```json')->before('```');
|
||||
} else if ($payload->contains('```')) {
|
||||
} elseif ($payload->contains('```')) {
|
||||
$payload = $payload->after('```')->before('```');
|
||||
}
|
||||
$payload = $payload->toString();
|
||||
$exception = null;
|
||||
|
||||
try {
|
||||
$newPayload = (new JsonFixer)->fix($payload);
|
||||
$newPayload = (new JsonFixer())->fix($payload);
|
||||
|
||||
return json_decode($newPayload, true);
|
||||
} catch (\Aws\Exception\InvalidJsonException $e) {
|
||||
$exception = $e;
|
||||
Log::warning("Invalid JSON, retrying:");
|
||||
Log::warning('Invalid JSON, retrying:');
|
||||
Log::warning($payload);
|
||||
$this->queryCompletion();
|
||||
}
|
||||
@@ -113,9 +124,10 @@ class GptCompleter
|
||||
$payload = Str::of($this->result)->trim();
|
||||
if ($payload->contains('```html')) {
|
||||
$payload = $payload->after('```html')->before('```');
|
||||
} else if ($payload->contains('```')) {
|
||||
} elseif ($payload->contains('```')) {
|
||||
$payload = $payload->after('```')->before('```');
|
||||
}
|
||||
|
||||
return $payload->toString();
|
||||
}
|
||||
|
||||
@@ -134,7 +146,7 @@ class GptCompleter
|
||||
if (isset($this->systemMessage) && $messages[0]['role'] !== 'system') {
|
||||
$messages = array_merge([[
|
||||
'role' => 'system',
|
||||
'content' => $this->systemMessage
|
||||
'content' => $this->systemMessage,
|
||||
]], $messages);
|
||||
}
|
||||
|
||||
@@ -142,16 +154,17 @@ class GptCompleter
|
||||
'model' => $this->model,
|
||||
'messages' => $messages,
|
||||
'max_tokens' => $maxTokens,
|
||||
'temperature' => $temperature
|
||||
'temperature' => $temperature,
|
||||
];
|
||||
|
||||
if ($this->expectsJson) {
|
||||
$completionInput['response_format'] = [
|
||||
'type' => 'json_object'
|
||||
'type' => 'json_object',
|
||||
];
|
||||
}
|
||||
|
||||
$this->completionInput = $completionInput;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -162,7 +175,7 @@ class GptCompleter
|
||||
}
|
||||
|
||||
try {
|
||||
Log::debug("Open AI query: " . json_encode($this->completionInput));
|
||||
Log::debug('Open AI query: '.json_encode($this->completionInput));
|
||||
$response = $this->openAi->chat()->create($this->completionInput);
|
||||
} catch (ErrorException $errorException) {
|
||||
// Retry once
|
||||
@@ -171,12 +184,13 @@ class GptCompleter
|
||||
}
|
||||
$this->tokenUsed += $response->usage->totalTokens;
|
||||
$this->result = $response->choices[0]->message->content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function queryStreamedCompletion(): self
|
||||
{
|
||||
Log::debug("Open AI query: " . json_encode($this->completionInput));
|
||||
Log::debug('Open AI query: '.json_encode($this->completionInput));
|
||||
$this->result = '';
|
||||
$response = $this->openAi->chat()->createStreamed($this->completionInput);
|
||||
foreach ($response as $chunk) {
|
||||
|
||||
@@ -53,13 +53,12 @@ class JsonFixer
|
||||
/**
|
||||
* Set/unset silent mode.
|
||||
*
|
||||
* @param bool $silent
|
||||
*
|
||||
* @param bool $silent
|
||||
* @return $this
|
||||
*/
|
||||
public function silent($silent = true)
|
||||
{
|
||||
$this->silent = (bool)$silent;
|
||||
$this->silent = (bool) $silent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -67,8 +66,7 @@ class JsonFixer
|
||||
/**
|
||||
* Set missing value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function missingValue($value)
|
||||
@@ -87,16 +85,15 @@ class JsonFixer
|
||||
/**
|
||||
* Fix the truncated JSON.
|
||||
*
|
||||
* @param string $json The JSON string to fix.
|
||||
*
|
||||
* @param string $json The JSON string to fix.
|
||||
* @return string Fixed JSON. If failed with silent then original JSON.
|
||||
* @throws InvalidJsonException When fixing fails.
|
||||
*
|
||||
* @throws InvalidJsonException When fixing fails.
|
||||
*/
|
||||
public function fix($json)
|
||||
{
|
||||
$json = preg_replace('/(?<!\\\\)(?:\\\\{2})*\p{C}+/u', '', $json);
|
||||
list($head, $json, $tail) = $this->trim($json);
|
||||
[$head, $json, $tail] = $this->trim($json);
|
||||
|
||||
if (empty($json) || $this->isValid($json)) {
|
||||
return $json;
|
||||
@@ -108,7 +105,7 @@ class JsonFixer
|
||||
|
||||
$this->reset();
|
||||
|
||||
return $head . $this->doFix($json) . $tail;
|
||||
return $head.$this->doFix($json).$tail;
|
||||
}
|
||||
|
||||
protected function trim($json)
|
||||
@@ -125,15 +122,15 @@ class JsonFixer
|
||||
|
||||
protected function isValid($json)
|
||||
{
|
||||
\json_decode($json,true,512,JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
\json_decode($json, true, 512, JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
|
||||
return \JSON_ERROR_NONE === \json_last_error();
|
||||
return \json_last_error() === \JSON_ERROR_NONE;
|
||||
}
|
||||
|
||||
protected function quickFix($json)
|
||||
{
|
||||
if (\strlen($json) === 1 && isset($this->pairs[$json])) {
|
||||
return $json . $this->pairs[$json];
|
||||
return $json.$this->pairs[$json];
|
||||
}
|
||||
|
||||
if ($json[0] !== '"') {
|
||||
@@ -153,7 +150,7 @@ class JsonFixer
|
||||
|
||||
protected function maybeLiteral($json)
|
||||
{
|
||||
if (!\in_array($json[0], ['t', 'f', 'n'])) {
|
||||
if (! \in_array($json[0], ['t', 'f', 'n'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -170,14 +167,14 @@ class JsonFixer
|
||||
|
||||
protected function doFix($json)
|
||||
{
|
||||
list($index, $char) = [-1, ''];
|
||||
[$index, $char] = [-1, ''];
|
||||
|
||||
while (isset($json[++$index])) {
|
||||
list($prev, $char) = [$char, $json[$index]];
|
||||
[$prev, $char] = [$char, $json[$index]];
|
||||
|
||||
$next = isset($json[$index + 1]) ? $json[$index + 1] : '';
|
||||
|
||||
if (!\in_array($char, [' ', "\n", "\r"])) {
|
||||
if (! \in_array($char, [' ', "\n", "\r"])) {
|
||||
$this->stack($prev, $char, $index, $next);
|
||||
}
|
||||
}
|
||||
@@ -212,7 +209,7 @@ class JsonFixer
|
||||
protected function popToken($token = null)
|
||||
{
|
||||
// Last one
|
||||
if (null === $token) {
|
||||
if ($token === null) {
|
||||
return \array_pop($this->stack);
|
||||
}
|
||||
|
||||
@@ -228,7 +225,7 @@ class JsonFixer
|
||||
protected function maybeStr($prev, $char, $index)
|
||||
{
|
||||
if ($prev !== '\\' && $char === '"') {
|
||||
$this->inStr = !$this->inStr;
|
||||
$this->inStr = ! $this->inStr;
|
||||
}
|
||||
|
||||
if ($this->inStr && $this->lastToken() !== '"') {
|
||||
@@ -267,7 +264,7 @@ class JsonFixer
|
||||
}
|
||||
|
||||
\Log::debug('Broken json received: ', [
|
||||
'json' => $json
|
||||
'json' => $json,
|
||||
]);
|
||||
|
||||
throw new InvalidJsonException(
|
||||
|
||||
@@ -16,7 +16,7 @@ trait PadsJson
|
||||
{
|
||||
public function pad($tmpJson)
|
||||
{
|
||||
if (!$this->inStr) {
|
||||
if (! $this->inStr) {
|
||||
$tmpJson = \rtrim($tmpJson, ',');
|
||||
while ($this->lastToken() === ',') {
|
||||
$this->popToken();
|
||||
@@ -37,11 +37,11 @@ trait PadsJson
|
||||
|
||||
$match = \preg_match('/(tr?u?e?|fa?l?s?e?|nu?l?l?)$/', $tmpJson, $matches);
|
||||
|
||||
if (!$match || null === $literal = $this->maybeLiteral($matches[1])) {
|
||||
if (! $match || null === $literal = $this->maybeLiteral($matches[1])) {
|
||||
return $tmpJson;
|
||||
}
|
||||
|
||||
return \substr($tmpJson, 0, 0 - \strlen($matches[1])) . $literal;
|
||||
return \substr($tmpJson, 0, 0 - \strlen($matches[1])).$literal;
|
||||
}
|
||||
|
||||
protected function padStack($tmpJson)
|
||||
@@ -57,7 +57,7 @@ trait PadsJson
|
||||
|
||||
protected function padObject($tmpJson)
|
||||
{
|
||||
if (!$this->objectNeedsPadding($tmpJson)) {
|
||||
if (! $this->objectNeedsPadding($tmpJson)) {
|
||||
return $tmpJson;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ trait PadsJson
|
||||
}
|
||||
|
||||
$tmpJson = $this->padIf($tmpJson, ':');
|
||||
$tmpJson = $tmpJson . $this->missingValue;
|
||||
$tmpJson = $tmpJson.$this->missingValue;
|
||||
|
||||
if ($this->lastToken() === '"') {
|
||||
$this->popToken();
|
||||
@@ -82,19 +82,19 @@ trait PadsJson
|
||||
|
||||
protected function objectNeedsPadding($tmpJson)
|
||||
{
|
||||
$last = \substr($tmpJson, -1);
|
||||
$empty = $last === '{' && !$this->inStr;
|
||||
$last = \substr($tmpJson, -1);
|
||||
$empty = $last === '{' && ! $this->inStr;
|
||||
|
||||
return !$empty && $this->arrayPos < $this->objectPos;
|
||||
return ! $empty && $this->arrayPos < $this->objectPos;
|
||||
}
|
||||
|
||||
protected function padString($string)
|
||||
{
|
||||
$last = \substr($string, -1);
|
||||
$last = \substr($string, -1);
|
||||
$last2 = \substr($string, -2);
|
||||
|
||||
if ($last2 === '\"' || $last !== '"') {
|
||||
return $string . '"';
|
||||
return $string.'"';
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
@@ -105,7 +105,7 @@ trait PadsJson
|
||||
protected function padIf($string, $substr)
|
||||
{
|
||||
if (\substr($string, 0 - \strlen($substr)) !== $substr) {
|
||||
return $string . $substr;
|
||||
return $string.$substr;
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
||||
@@ -25,7 +25,7 @@ class SeoMetaResolver
|
||||
{
|
||||
private array $patternData = [];
|
||||
|
||||
const URL_PATTERNS = [
|
||||
public const URL_PATTERNS = [
|
||||
'welcome' => '/',
|
||||
'form_show' => '/forms/{slug}',
|
||||
'login' => '/login',
|
||||
@@ -43,7 +43,7 @@ class SeoMetaResolver
|
||||
/**
|
||||
* Metas for simple route (without needing to access DB)
|
||||
*/
|
||||
const PATTERN_STATIC_META = [
|
||||
public const PATTERN_STATIC_META = [
|
||||
'login' => [
|
||||
'title' => 'Login',
|
||||
],
|
||||
@@ -64,13 +64,13 @@ class SeoMetaResolver
|
||||
],
|
||||
'templates' => [
|
||||
'title' => 'Templates',
|
||||
'description' => 'Our collection of beautiful templates to create your own forms!'
|
||||
'description' => 'Our collection of beautiful templates to create your own forms!',
|
||||
],
|
||||
];
|
||||
|
||||
const META_CACHE_DURATION = 60 * 60 * 12; // 12 hours
|
||||
public const META_CACHE_DURATION = 60 * 60 * 12; // 12 hours
|
||||
|
||||
const META_CACHE_KEY_PREFIX = 'seo_meta_';
|
||||
public const META_CACHE_KEY_PREFIX = 'seo_meta_';
|
||||
|
||||
public function __construct(private Request $request)
|
||||
{
|
||||
@@ -78,12 +78,10 @@ class SeoMetaResolver
|
||||
|
||||
/**
|
||||
* Returns the right metas for a given route, caches meta for 1 hour.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMetas(): array
|
||||
{
|
||||
$cacheKey = self::META_CACHE_KEY_PREFIX . urlencode($this->request->path());
|
||||
$cacheKey = self::META_CACHE_KEY_PREFIX.urlencode($this->request->path());
|
||||
|
||||
return Cache::remember($cacheKey, now()->addSeconds(self::META_CACHE_DURATION), function () {
|
||||
$pattern = $this->resolvePattern();
|
||||
@@ -91,7 +89,7 @@ class SeoMetaResolver
|
||||
if ($this->hasPatternMetaGetter($pattern)) {
|
||||
// Custom function for pattern
|
||||
try {
|
||||
return array_merge($this->getDefaultMeta(), $this->{'get' . Str::studly($pattern) . 'Meta'}());
|
||||
return array_merge($this->getDefaultMeta(), $this->{'get'.Str::studly($pattern).'Meta'}());
|
||||
} catch (\Exception $e) {
|
||||
return $this->getDefaultMeta();
|
||||
}
|
||||
@@ -122,7 +120,7 @@ class SeoMetaResolver
|
||||
foreach (self::URL_PATTERNS as $patternName => $patternData) {
|
||||
$path = rtrim($this->request->getPathInfo(), '/') ?: '/';
|
||||
|
||||
$route = (new Route('GET', $patternData, fn() => ''))->bind($this->request);
|
||||
$route = (new Route('GET', $patternData, fn () => ''))->bind($this->request);
|
||||
if (preg_match($route->getCompiled()->getRegex(), rawurldecode($path))) {
|
||||
$this->patternData = $route->parameters();
|
||||
|
||||
@@ -136,23 +134,23 @@ class SeoMetaResolver
|
||||
/**
|
||||
* Determine if a get mutator exists for a pattern.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
private function hasPatternMetaGetter($key)
|
||||
{
|
||||
return method_exists($this, 'get' . Str::studly($key) . 'Meta');
|
||||
return method_exists($this, 'get'.Str::studly($key).'Meta');
|
||||
}
|
||||
|
||||
private function titleSuffix()
|
||||
{
|
||||
return ' · ' . config('app.name');
|
||||
return ' · '.config('app.name');
|
||||
}
|
||||
|
||||
private function getDefaultMeta(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Create beautiful forms for free' . $this->titleSuffix(),
|
||||
'title' => 'Create beautiful forms for free'.$this->titleSuffix(),
|
||||
'description' => "Create beautiful forms for free. Unlimited fields, unlimited submissions. It's free and it takes less than 1 minute to create your first form.",
|
||||
'image' => asset('/img/social-preview.jpg'),
|
||||
];
|
||||
@@ -166,21 +164,21 @@ class SeoMetaResolver
|
||||
if ($form->is_pro && $form->seo_meta->page_title) {
|
||||
$meta['title'] = $form->seo_meta->page_title;
|
||||
} else {
|
||||
$meta['title'] = $form->title . $this->titleSuffix();
|
||||
$meta['title'] = $form->title.$this->titleSuffix();
|
||||
}
|
||||
|
||||
if ($form->is_pro && $form->seo_meta->page_description) {
|
||||
$meta['description'] = $form->seo_meta->page_description;
|
||||
} else if ($form->description) {
|
||||
} elseif ($form->description) {
|
||||
$meta['description'] = Str::of($form->description)->limit(160);
|
||||
}
|
||||
|
||||
if ($form->is_pro && $form->seo_meta->page_thumbnail) {
|
||||
$meta['image'] = $form->seo_meta->page_thumbnail;
|
||||
} else if ($form->cover_picture) {
|
||||
} elseif ($form->cover_picture) {
|
||||
$meta['image'] = $form->cover_picture;
|
||||
}
|
||||
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
@@ -189,9 +187,9 @@ class SeoMetaResolver
|
||||
$template = Template::whereSlug($this->patternData['slug'])->firstOrFail();
|
||||
|
||||
return [
|
||||
'title' => $template->name . $this->titleSuffix(),
|
||||
'description' => Str::of($template->short_description)->limit(140) . ' | Customize any template and create your own form in minutes.',
|
||||
'image' => $template->image_url
|
||||
'title' => $template->name.$this->titleSuffix(),
|
||||
'description' => Str::of($template->short_description)->limit(140).' | Customize any template and create your own form in minutes.',
|
||||
'image' => $template->image_url,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ use Illuminate\Support\Str;
|
||||
class StorageFileNameParser
|
||||
{
|
||||
public ?string $uuid = null;
|
||||
|
||||
public ?string $fileName = null;
|
||||
|
||||
public ?string $extension = null;
|
||||
|
||||
public function __construct(string $fileName)
|
||||
@@ -24,15 +26,16 @@ class StorageFileNameParser
|
||||
/**
|
||||
* If we have parsed a file name and an extension, we keep the same and append uuid to avoid collisions
|
||||
* Otherwise we just return the uuid
|
||||
* @return string
|
||||
*/
|
||||
public function getMovedFileName(): ?string
|
||||
{
|
||||
if ($this->fileName && $this->extension) {
|
||||
$fileName = substr($this->fileName, 0, 50).'_'.$this->uuid.'.'.$this->extension;
|
||||
$fileName = preg_replace('#\p{C}+#u', '', $fileName); // avoid CorruptedPathDetected exceptions
|
||||
|
||||
return mb_convert_encoding($fileName, 'UTF-8', 'UTF-8');
|
||||
}
|
||||
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
@@ -40,16 +43,17 @@ class StorageFileNameParser
|
||||
{
|
||||
if (Str::isUuid($fileName)) {
|
||||
$this->uuid = $fileName;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!str_contains($fileName, '_')) {
|
||||
if (! str_contains($fileName, '_')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$candidateString = substr($fileName, strrpos($fileName, '_') + 1);
|
||||
if (!str_contains($candidateString, '.')
|
||||
|| !Str::isUuid(substr($candidateString, 0, strpos($candidateString, '.')))) {
|
||||
if (! str_contains($candidateString, '.')
|
||||
|| ! Str::isUuid(substr($candidateString, 0, strpos($candidateString, '.')))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,5 +71,4 @@ class StorageFileNameParser
|
||||
{
|
||||
return new self($fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
|
||||
use App\Models\Workspace;
|
||||
|
||||
class WorkspaceHelper
|
||||
{
|
||||
|
||||
public function __construct(public Workspace $workspace)
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user