* [Feature] Added Discord Webhook feature to forms * Remove commented out svg --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
eb3a11c992
commit
b101a5daba
|
@ -42,6 +42,7 @@ abstract class UserFormRequest extends \Illuminate\Foundation\Http\FormRequest
|
||||||
'webhook_url' => 'url|nullable',
|
'webhook_url' => 'url|nullable',
|
||||||
'use_captcha' => 'boolean',
|
'use_captcha' => 'boolean',
|
||||||
'slack_webhook_url' => 'url|nullable',
|
'slack_webhook_url' => 'url|nullable',
|
||||||
|
'discord_webhook_url' => 'url|nullable',
|
||||||
|
|
||||||
// Customization
|
// Customization
|
||||||
'theme' => ['required',Rule::in(Form::THEMES)],
|
'theme' => ['required',Rule::in(Form::THEMES)],
|
||||||
|
|
|
@ -30,6 +30,7 @@ class FormResource extends JsonResource
|
||||||
'submissions_count' => $this->when($this->workspaceIsPro(), $this->submissions_count),
|
'submissions_count' => $this->when($this->workspaceIsPro(), $this->submissions_count),
|
||||||
'notifies' => $this->notifies,
|
'notifies' => $this->notifies,
|
||||||
'notifies_slack' => $this->notifies_slack,
|
'notifies_slack' => $this->notifies_slack,
|
||||||
|
'notifies_discord' => $this->notifies_discord,
|
||||||
'send_submission_confirmation' => $this->send_submission_confirmation,
|
'send_submission_confirmation' => $this->send_submission_confirmation,
|
||||||
'webhook_url' => $this->webhook_url,
|
'webhook_url' => $this->webhook_url,
|
||||||
'redirect_url' => $this->redirect_url,
|
'redirect_url' => $this->redirect_url,
|
||||||
|
@ -45,6 +46,7 @@ class FormResource extends JsonResource
|
||||||
'visibility' => $this->visibility,
|
'visibility' => $this->visibility,
|
||||||
'notification_emails' => $this->notification_emails,
|
'notification_emails' => $this->notification_emails,
|
||||||
'slack_webhook_url' => $this->slack_webhook_url,
|
'slack_webhook_url' => $this->slack_webhook_url,
|
||||||
|
'discord_webhook_url' => $this->discord_webhook_url,
|
||||||
'removed_properties' => $this->removed_properties,
|
'removed_properties' => $this->removed_properties,
|
||||||
'last_edited_human' => $this->updated_at?->diffForHumans()
|
'last_edited_human' => $this->updated_at?->diffForHumans()
|
||||||
] : [];
|
] : [];
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
|
|
||||||
namespace App\Listeners\Forms;
|
namespace App\Listeners\Forms;
|
||||||
|
|
||||||
|
use App\Models\Forms\Form;
|
||||||
use App\Events\Forms\FormSubmitted;
|
use App\Events\Forms\FormSubmitted;
|
||||||
use App\Notifications\Forms\FormSubmissionNotification;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
use Spatie\WebhookServer\WebhookCall;
|
use Spatie\WebhookServer\WebhookCall;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
use App\Service\Forms\FormSubmissionFormatter;
|
use App\Service\Forms\FormSubmissionFormatter;
|
||||||
|
use App\Notifications\Forms\FormSubmissionNotification;
|
||||||
use Vinkla\Hashids\Facades\Hashids;
|
use Vinkla\Hashids\Facades\Hashids;
|
||||||
|
|
||||||
class NotifyFormSubmission implements ShouldQueue
|
class NotifyFormSubmission implements ShouldQueue
|
||||||
|
@ -44,8 +46,16 @@ class NotifyFormSubmission implements ShouldQueue
|
||||||
// Send Slack Notification
|
// Send Slack Notification
|
||||||
$this->sendSlackNotification($event);
|
$this->sendSlackNotification($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($event->form->notifies_discord) {
|
||||||
|
// Send Discord Notification
|
||||||
|
$this->sendDiscordNotification($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function sendSlackNotification(FormSubmitted $event)
|
private function sendSlackNotification(FormSubmitted $event)
|
||||||
{
|
{
|
||||||
if($this->validateSlackWebhookUrl($event->form->slack_webhook_url)){
|
if($this->validateSlackWebhookUrl($event->form->slack_webhook_url)){
|
||||||
|
@ -105,4 +115,75 @@ class NotifyFormSubmission implements ShouldQueue
|
||||||
{
|
{
|
||||||
return ($url) ? str_contains($url, 'https://hooks.slack.com/') : false;
|
return ($url) ? str_contains($url, 'https://hooks.slack.com/') : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendDiscordNotification(FormSubmitted $event)
|
||||||
|
{
|
||||||
|
if($this->validateDiscordWebhookUrl($event->form->discord_webhook_url)){
|
||||||
|
$submissionString = "";
|
||||||
|
$formatter = (new FormSubmissionFormatter($event->form, $event->data))->outputStringsOnly();
|
||||||
|
|
||||||
|
foreach ($formatter->getFieldsWithValue() as $field) {
|
||||||
|
$tmpVal = is_array($field['value']) ? implode(",", $field['value']) : $field['value'];
|
||||||
|
$submissionString .= "**".ucfirst($field['name'])."**: `".$tmpVal."`\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$form_name = $event->form->title;
|
||||||
|
$form = Form::find($event->form->id);
|
||||||
|
$formURL = url("forms/".$event->form->slug."/show/submissions");
|
||||||
|
|
||||||
|
$finalDiscordPostData = [
|
||||||
|
"content" => "@here We have received a new submission for **$form_name**",
|
||||||
|
"username" => config('app.name'),
|
||||||
|
"avatar_url" => asset('img/logo.png'),
|
||||||
|
"tts" => false,
|
||||||
|
"embeds" => [
|
||||||
|
[
|
||||||
|
"title" => "🔗 Go to $form_name",
|
||||||
|
|
||||||
|
"type" => "rich",
|
||||||
|
|
||||||
|
"description" => $submissionString,
|
||||||
|
|
||||||
|
"url" => $formURL,
|
||||||
|
|
||||||
|
"color" => hexdec(str_replace('#', '', $event->form->color)),
|
||||||
|
|
||||||
|
"footer" => [
|
||||||
|
"text" => config('app.name'),
|
||||||
|
"icon_url" => asset('img/logo.png'),
|
||||||
|
],
|
||||||
|
|
||||||
|
"author" => [
|
||||||
|
"name" => config('app.name'),
|
||||||
|
"url" => config('app.url'),
|
||||||
|
],
|
||||||
|
|
||||||
|
"fields" => [
|
||||||
|
[
|
||||||
|
"name" => "Views 👀",
|
||||||
|
"value" => "$form->views_count",
|
||||||
|
"inline" => true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Submissions 🖊️",
|
||||||
|
"value" => "$form->submissions_count",
|
||||||
|
"inline" => true
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
WebhookCall::create()
|
||||||
|
->url($event->form->discord_webhook_url)
|
||||||
|
->doNotSign()
|
||||||
|
->payload($finalDiscordPostData)
|
||||||
|
->dispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateDiscordWebhookUrl($url)
|
||||||
|
{
|
||||||
|
return ($url) ? str_contains($url, 'https://discord.com/api/webhooks') : false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ class Form extends Model
|
||||||
'notification_body',
|
'notification_body',
|
||||||
'notifications_include_submission',
|
'notifications_include_submission',
|
||||||
'slack_webhook_url',
|
'slack_webhook_url',
|
||||||
|
'discord_webhook_url',
|
||||||
|
|
||||||
// integrations
|
// integrations
|
||||||
'webhook_url',
|
'webhook_url',
|
||||||
|
@ -98,6 +99,7 @@ class Form extends Model
|
||||||
'workspace_id',
|
'workspace_id',
|
||||||
'notifies',
|
'notifies',
|
||||||
'slack_webhook_url',
|
'slack_webhook_url',
|
||||||
|
'discord_webhook_url',
|
||||||
'webhook_url',
|
'webhook_url',
|
||||||
'send_submission_confirmation',
|
'send_submission_confirmation',
|
||||||
'redirect_url',
|
'redirect_url',
|
||||||
|
@ -250,6 +252,12 @@ class Form extends Model
|
||||||
return FormFactory::new();
|
return FormFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getNotifiesDiscordAttribute()
|
||||||
|
{
|
||||||
|
return !empty($this->discord_webhook_url);
|
||||||
|
}
|
||||||
|
|
||||||
public function getNotifiesSlackAttribute()
|
public function getNotifiesSlackAttribute()
|
||||||
{
|
{
|
||||||
return !empty($this->slack_webhook_url);
|
return !empty($this->slack_webhook_url);
|
||||||
|
|
|
@ -39,6 +39,7 @@ class FormCleaner
|
||||||
'use_captcha' => false,
|
'use_captcha' => false,
|
||||||
'password' => null,
|
'password' => null,
|
||||||
'slack_webhook_url' => null,
|
'slack_webhook_url' => null,
|
||||||
|
'discord_webhook_url' => null,
|
||||||
];
|
];
|
||||||
|
|
||||||
private array $fieldDefaults = [
|
private array $fieldDefaults = [
|
||||||
|
@ -70,6 +71,7 @@ class FormCleaner
|
||||||
'database_fields_update' => 'Form submission will only create new records (no updates).',
|
'database_fields_update' => 'Form submission will only create new records (no updates).',
|
||||||
'theme' => 'Default theme was applied.',
|
'theme' => 'Default theme was applied.',
|
||||||
'slack_webhook_url' => "Slack webhook disabled.",
|
'slack_webhook_url' => "Slack webhook disabled.",
|
||||||
|
'discord_webhook_url' => "Discord webhook disabled.",
|
||||||
|
|
||||||
// For fields
|
// For fields
|
||||||
'hide_field_name' => 'Hide field name removed.',
|
'hide_field_name' => 'Hide field name removed.',
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('forms', function (Blueprint $table) {
|
||||||
|
$table->string('discord_webhook_url')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('forms', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('discord_webhook_url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
<form-notifications-option />
|
<form-notifications-option />
|
||||||
<form-notifications-slack />
|
<form-notifications-slack />
|
||||||
|
<form-notifications-discord />
|
||||||
<form-notifications-submission-confirmation />
|
<form-notifications-submission-confirmation />
|
||||||
|
|
||||||
</collapse>
|
</collapse>
|
||||||
|
@ -24,10 +25,11 @@ import Collapse from '../../../../common/Collapse.vue'
|
||||||
import ProTag from '../../../../common/ProTag.vue'
|
import ProTag from '../../../../common/ProTag.vue'
|
||||||
import FormNotificationsOption from './components/FormNotificationsOption.vue'
|
import FormNotificationsOption from './components/FormNotificationsOption.vue'
|
||||||
import FormNotificationsSlack from './components/FormNotificationsSlack.vue'
|
import FormNotificationsSlack from './components/FormNotificationsSlack.vue'
|
||||||
|
import FormNotificationsDiscord from './components/FormNotificationsDiscord.vue'
|
||||||
import FormNotificationsSubmissionConfirmation from './components/FormNotificationsSubmissionConfirmation.vue'
|
import FormNotificationsSubmissionConfirmation from './components/FormNotificationsSubmissionConfirmation.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { FormNotificationsSubmissionConfirmation, FormNotificationsSlack, FormNotificationsOption, Collapse, ProTag },
|
components: { FormNotificationsSubmissionConfirmation, FormNotificationsSlack, FormNotificationsDiscord, FormNotificationsOption, Collapse, ProTag },
|
||||||
props: {
|
props: {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
|
||||||
|
@click.prevent="showModal=true"
|
||||||
|
>
|
||||||
|
<div class="flex-grow flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 inline" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M7.5 7.5c3.5 -1 5.5 -1 9 0"></path><path d="M7 16.5c3.5 1 6.5 1 10 0"></path><path d="M15.5 17c0 1 1.5 3 2 3c1.5 0 2.833 -1.667 3.5 -3c.667 -1.667 .5 -5.833 -1.5 -11.5c-1.457 -1.015 -3 -1.34 -4.5 -1.5l-1 2.5"></path><path d="M8.5 17c0 1 -1.356 3 -1.832 3c-1.429 0 -2.698 -1.667 -3.333 -3c-.635 -1.667 -.476 -5.833 1.428 -11.5c1.388 -1.015 2.782 -1.34 4.237 -1.5l1 2.5"></path></svg>
|
||||||
|
<p class="flex-grow text-center">
|
||||||
|
Discord Notifications
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="form.notifies_discord">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||||
|
class="w-5 h-5 text-nt-blue"
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<modal :show="showModal" @close="showModal=false">
|
||||||
|
<h2 class="text-2xl font-bold z-10 truncate mb-5 text-nt-blue">
|
||||||
|
Discord Notifications
|
||||||
|
<pro-tag />
|
||||||
|
</h2>
|
||||||
|
<toggle-switch-input name="notifies_discord" :form="form" class="mt-4"
|
||||||
|
label="Receive a Discord notification on submission"
|
||||||
|
/>
|
||||||
|
<text-input v-if="form.notifies_discord" name="discord_webhook_url" :form="form" class="mt-4"
|
||||||
|
label="Discord webhook url" help="help"
|
||||||
|
>
|
||||||
|
<template #help>
|
||||||
|
Receive a discord message on each form submission.
|
||||||
|
<a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" target="_blank">Click here</a> to learn how to get a discord webhook url.
|
||||||
|
</template>
|
||||||
|
</text-input>
|
||||||
|
</modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ProTag from '../../../../../common/ProTag.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ProTag },
|
||||||
|
props: {},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
showModal: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
form: {
|
||||||
|
get () {
|
||||||
|
return this.$store.state['open/working_form'].content
|
||||||
|
},
|
||||||
|
/* We add a setter */
|
||||||
|
set (value) {
|
||||||
|
this.$store.commit('open/working_form/set', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {}
|
||||||
|
}
|
||||||
|
</script>
|
Loading…
Reference in New Issue