Migrate front-end to Nuxt app (#284)
* wip * Managed to load a page * Stuck at changing routes * Fixed the router, and editable div * WIP * Fix app loader * WIP * Fix check-auth middleware * Started to refactor input components * WIP * Added select input, v-click-outside for vselect * update vselect & phone input * Fixed the mixin * input component updates * Fix signature input import * input component updates in vue3 * image input in vue3 * small fixes * fix useFormInput watcher * scale input in vue3 * Vue3: migrating from vuex to Pinia (#249) * Vue3: migrating from vuex to Pinia * toggle input fixes * update configureCompat --------- Co-authored-by: Forms Dev <chirag+new@notionforms.io> * support vue3 query builder * Refactor inpus * fix: Vue3 Query Builder - Logic Editor (#251) * support vue3 query builder * upgrade * remove local from middleware * Submission table pagination & migrate chart to vue3 (#254) * Submission table Pagination in background * migrate chart to vue3 * Form submissions pagination * Form submissions * Fix form starts * Fix openSelect key issue --------- Co-authored-by: Forms Dev <chirag+new@notionforms.io> Co-authored-by: Julien Nahum <julien@nahum.net> * Vue 3 better animation (#257) * vue-3-better-animation * Working on migration to vueuse/motion * Form sidebar animations * Clean code * Added animations for modal * Finished implementing better animations --------- Co-authored-by: Forms Dev <chirag+new@notionforms.io> * Work in progress * Migrating amplitude and crisp plugin/composable * Started to refactor pages * WIP * vue3-scroll-shadow-fixes (#260) * WIP * WIP * WIP * Figured out auth & middlewares * WI * Refactoring stores and templates pages to comp. api * Finishing the templates pages * fix collapsible * Finish reworking most templates pages * Reworked workspaces store * Working on home page and modal * Fix dropdown * Fix modal * Fixed form creation * Fixed most of the form/show pages * Updated cors dependency * fix custom domain warning * NuxtLink migration (#262) Co-authored-by: Forms Dev <chirag+new@notionforms.io> * Tiny fixes + start pre-rendering * migrate-to-nuxt-useappconfig (#263) * migrate-to-nuxt-useappconfig * defineAppConfig --------- Co-authored-by: Forms Dev <chirag+new@notionforms.io> * Working on form/show and editor * Globally import form inputs to fix resolve * Remove vform - working on form public page * Remove initform mixin * Work in progress for form create guess user * Nuxt Migration notifications (#265) * Nuxt Migration notifications * @input to @update:model-value * change field type fixes * @update:model-value * Enable form-block-logic-editor * vue-confetti migration * PR request changes * useAlert in setup * Migrate to nuxt settings page AND remove axios (#266) * Settings pages migration * remove axios and use opnFetch * Make created form reactive (#267) * Remove verify pages and axios lib --------- Co-authored-by: Julien Nahum <julien@nahum.net> * Fix alert styling + bug fixes and cleaning * Refactor notifications + add shadow * Fix vselect issue * Working on page pre-rendering * Created NotionPages store * Added sitemap on nuxt side * Sitemap done, working on aws amplify * Adding missing module * Remove axios and commit backend changes to sitemap * Fix notifications * fix guestpage editor (#269) Co-authored-by: Julien Nahum <julien@nahum.net> * Remove appconfig in favor of runtimeconfig * Fixed amplitude bugs, and added staging environment * Added amplify file * Change basdirectory amplify * Fix loading bar position * Fix custom redirect (#273) * Dirty form handling - nuxt migration (#272) * SEO meta nuxt migration (#274) * SEO meta nuxt migration * Polish seo metas, add defaults for OG and twitter --------- Co-authored-by: Julien Nahum <julien@nahum.net> * migrate to nuxt useClipboard (#268) * Set middleware on pages (#278) * Se middleware on pages * Se middleware on account page * add robots.txt (#276) * 404 page migration (#277) * Templates pages migration (#275) * NuxtImg Migration (#279) Co-authored-by: Julien Nahum <julien@nahum.net> * Update package json * Fix build script * Add loglevel param * Disable page pre-rendering * Attempt to allow svgs * Fix SVGs with NuxtImage * Add .env file at AWS build time * tRGIGGER deploy * Fix issue * ANother attrempt * Fix typo * Fix env? * Attempt to simplify build * Enable swr caching instead of prerenderign * Better image compression * Last attempt at nuxt images efficiency * Improve image optimization again * Remove NuxtImg for non asset files * Restore templates pages cache * Remove useless images + fix templates show page * image optimization caching + fix hydratation issue form template page * URL generation (front&back) + fixed authJWT for SSR * Fix composable issue * Fix form share page * Embeddable form as a nuxt middleware * Fix URL for embeddable middleware * Debugging embeddable on amplify * Add custom domain support * No follow for non-production env * Fix sentry nuxt and custom domain redirect * remove api prefix from routes (#280) * remove api prefix from routes * PR changes --------- Co-authored-by: Julien Nahum <julien@nahum.net> * nuxt migration -file upload - WIP (#271) Co-authored-by: Julien Nahum <julien@nahum.net> * Fix local file upload * Fix file submissions preview * API redirect to back-end from nuxt * API redirect to back-end from nuxt * Remove old JS app, update deploy script * Fix tests, added gh action nuxt step * Updated package-lock.json * Setup node in GH Nuxt action * Setup client directory for GH workflow --------- Co-authored-by: Forms Dev <chirag+new@notionforms.io> Co-authored-by: Chirag Chhatrala <60499540+chiragchhatrala@users.noreply.github.com> Co-authored-by: Rishi Raj Jain <rishi18304@iiitd.ac.in> Co-authored-by: formsdev <136701234+formsdev@users.noreply.github.com>
This commit is contained in:
@@ -48,7 +48,7 @@ class Handler extends ExceptionHandler
|
||||
{
|
||||
return $request->expectsJson()
|
||||
? response()->json(['message' => $exception->getMessage()], 401)
|
||||
: redirect()->guest(url('/login'));
|
||||
: redirect(front_url('login'));
|
||||
}
|
||||
|
||||
public function report(Throwable $exception)
|
||||
|
||||
@@ -28,10 +28,10 @@ class AppSumoAuthController extends Controller
|
||||
|
||||
// otherwise start login flow by passing the encrypted license key id
|
||||
if (is_null($license->user_id)) {
|
||||
return redirect(url('/register?appsumo_license='.encrypt($license->id)));
|
||||
return redirect(front_url('/register?appsumo_license='.encrypt($license->id)));
|
||||
}
|
||||
|
||||
return redirect(url('/register?appsumo_error=1'));
|
||||
return redirect(front_url('/register?appsumo_error=1'));
|
||||
}
|
||||
|
||||
private function retrieveAccessToken(string $requestCode): string
|
||||
@@ -82,11 +82,11 @@ class AppSumoAuthController extends Controller
|
||||
if (is_null($license->user_id)) {
|
||||
$license->user_id = Auth::id();
|
||||
$license->save();
|
||||
return redirect(url('/home?appsumo_connect=1'));
|
||||
return redirect(front_url('/home?appsumo_connect=1'));
|
||||
}
|
||||
|
||||
// Licensed already attached
|
||||
return redirect(url('/home?appsumo_error=1'));
|
||||
return redirect(front_url('/home?appsumo_error=1'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ class FileUploadController extends Controller
|
||||
*/
|
||||
public function upload(Request $request)
|
||||
{
|
||||
$request->validate(['file' => 'required|file']);
|
||||
$uuid = (string) Str::uuid();
|
||||
$path = $request->file('file')->storeAs(PublicFormController::TMP_FILE_UPLOAD_PATH, $uuid);
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ class FormSubmissionController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->middleware('auth', ['except' => ['submissionFile']]);
|
||||
$this->middleware('signed', ['only' => ['submissionFile']]);
|
||||
}
|
||||
|
||||
public function submissions(string $id)
|
||||
@@ -51,9 +52,6 @@ class FormSubmissionController extends Controller
|
||||
|
||||
public function submissionFile($id, $fileName)
|
||||
{
|
||||
$form = Form::findOrFail((int) $id);
|
||||
$this->authorize('view', $form);
|
||||
|
||||
$fileName = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $id).'/'
|
||||
.urldecode($fileName);
|
||||
|
||||
@@ -63,8 +61,12 @@ class FormSubmissionController extends Controller
|
||||
], 404);
|
||||
}
|
||||
|
||||
if (config('filesystems.default') !== 's3') {
|
||||
return response()->file(Storage::path($fileName));
|
||||
}
|
||||
|
||||
return redirect(
|
||||
Storage::temporaryUrl($fileName, now()->addMinute())
|
||||
Storage::temporaryUrl($fileName, now()->addMinute())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,62 +9,24 @@ use App\Models\Template;
|
||||
|
||||
class SitemapController extends Controller
|
||||
{
|
||||
/**
|
||||
* Contains route name and the associated priority
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $urls = [
|
||||
['/', 1],
|
||||
['/pricing', 0.9],
|
||||
['/privacy-policy', 0.5],
|
||||
['/terms-conditions', 0.5],
|
||||
['/login', 0.4],
|
||||
['/register', 0.4],
|
||||
['/password/reset', 0.3],
|
||||
['/form-templates', 0.9],
|
||||
];
|
||||
|
||||
public function getSitemap(Request $request)
|
||||
public function index(Request $request)
|
||||
{
|
||||
$sitemap = Sitemap::create();
|
||||
foreach ($this->urls as $url) {
|
||||
$sitemap->add($this->createUrl($url[0], $url[1]));
|
||||
}
|
||||
$this->addTemplatesUrls($sitemap);
|
||||
$this->addTemplatesTypesUrls($sitemap);
|
||||
$this->addTemplatesIndustriesUrls($sitemap);
|
||||
|
||||
return $sitemap->toResponse($request);
|
||||
return [
|
||||
...$this->getTemplatesUrls()
|
||||
];
|
||||
}
|
||||
|
||||
private function createUrl($url, $priority, $frequency = 'daily')
|
||||
private function getTemplatesUrls()
|
||||
{
|
||||
return Url::create($url)->setPriority($priority)->setChangeFrequency($frequency);
|
||||
}
|
||||
|
||||
private function addTemplatesUrls(Sitemap $sitemap)
|
||||
{
|
||||
Template::where('publicly_listed', true)->chunk(100, function ($templates) use ($sitemap) {
|
||||
$urls = [];
|
||||
Template::where('publicly_listed', true)->chunk(100, function ($templates) use (&$urls) {
|
||||
foreach ($templates as $template) {
|
||||
$sitemap->add($this->createUrl('/form-templates/' . $template->slug, 0.8));
|
||||
$urls[] = [
|
||||
'loc' => '/templates/' . $template->slug
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function addTemplatesTypesUrls(Sitemap $sitemap)
|
||||
{
|
||||
$types = json_decode(file_get_contents(resource_path('data/forms/templates/types.json')), true);
|
||||
foreach ($types as $type) {
|
||||
$sitemap->add($this->createUrl('/form-templates/types/' . $type['slug'], 0.7));
|
||||
}
|
||||
}
|
||||
|
||||
private function addTemplatesIndustriesUrls(Sitemap $sitemap)
|
||||
{
|
||||
$industries = json_decode(file_get_contents(resource_path('data/forms/templates/industries.json')), true);
|
||||
foreach ($industries as $industry) {
|
||||
$sitemap->add($this->createUrl('/form-templates/industries/' . $industry['slug'], 0.7));
|
||||
}
|
||||
return $urls;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Service\SeoMetaResolver;
|
||||
use Illuminate\Http\Request;
|
||||
class SpaController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get the SPA view.
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
return view('spa',[
|
||||
'meta' => (new SeoMetaResolver($request))->getMetas(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,8 @@ class SubscriptionController extends Controller
|
||||
$checkout = $checkoutBuilder
|
||||
->collectTaxIds()
|
||||
->checkout([
|
||||
'success_url' => url('/subscriptions/success'),
|
||||
'cancel_url' => url('/subscriptions/error'),
|
||||
'success_url' => front_url('/subscriptions/success'),
|
||||
'cancel_url' => front_url('/subscriptions/error'),
|
||||
'billing_address_collection' => 'required',
|
||||
'customer_update' => [
|
||||
'address' => 'auto',
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\AuthenticateJWT;
|
||||
use App\Http\Middleware\CustomDomainRestriction;
|
||||
use App\Http\Middleware\EmbeddableForms;
|
||||
use App\Http\Middleware\IsAdmin;
|
||||
use App\Http\Middleware\IsNotSubscribed;
|
||||
use App\Http\Middleware\IsSubscribed;
|
||||
@@ -20,9 +19,9 @@ class Kernel extends HttpKernel
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
@@ -46,16 +45,14 @@ class Kernel extends HttpKernel
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
EmbeddableForms::class
|
||||
],
|
||||
|
||||
'spa' => [
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
EmbeddableForms::class
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:60,1',
|
||||
'throttle:100,1',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
|
||||
@@ -15,7 +15,7 @@ class Authenticate extends Middleware
|
||||
protected function redirectTo($request)
|
||||
{
|
||||
if (! $request->expectsJson()) {
|
||||
return redirect('/login');
|
||||
return redirect(front_url('login'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use Tymon\JWTAuth\Exceptions\JWTException;
|
||||
|
||||
class AuthenticateJWT
|
||||
{
|
||||
const API_SERVER_SECRET_HEADER_NAME = 'x-api-secret';
|
||||
|
||||
/**
|
||||
* Verifies the JWT token and validates the IP and User Agent
|
||||
@@ -24,6 +25,13 @@ class AuthenticateJWT
|
||||
|
||||
// Validate IP and User Agent
|
||||
if ($payload) {
|
||||
if ($frontApiSecret = $request->header(self::API_SERVER_SECRET_HEADER_NAME)) {
|
||||
// If it's a trusted SSR request, skip the rest
|
||||
if ($frontApiSecret === config('app.front_api_secret')) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
$error = null;
|
||||
if (!\Hash::check($request->ip(), $payload->get('ip'))) {
|
||||
$error = 'Origin IP is invalid';
|
||||
|
||||
@@ -11,7 +11,7 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class CustomDomainRestriction
|
||||
{
|
||||
const CUSTOM_DOMAIN_HEADER = "User-Custom-Domain";
|
||||
const CUSTOM_DOMAIN_HEADER = "x-custom-domain";
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
@@ -27,7 +27,8 @@ class CustomDomainRestriction
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Invalid domain',
|
||||
], 400);
|
||||
'error' => 'invalid_domain',
|
||||
], 420);
|
||||
}
|
||||
|
||||
// Check if domain is different from current domain
|
||||
@@ -41,7 +42,8 @@ class CustomDomainRestriction
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Unknown domain',
|
||||
], 400);
|
||||
'error' => 'invalid_domain',
|
||||
], 420);
|
||||
}
|
||||
|
||||
Workspace::addGlobalScope('domain-restricted', function (Builder $builder) use ($workspace) {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class EmbeddableForms
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->expectsJson() || $request->wantsJson()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
if (!str_starts_with($request->url(), url('/forms/'))) {
|
||||
if ($response instanceof Response) {
|
||||
$response->header('X-Frame-Options', 'SAMEORIGIN');
|
||||
} elseif ($response instanceof \Symfony\Component\HttpFoundation\Response) {
|
||||
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,11 @@ class FormSubmissionResource extends JsonResource
|
||||
return $file !== null && $file;
|
||||
})->map(function ($file) {
|
||||
return [
|
||||
'file_url' => route('open.forms.submissions.file', [$this->form_id, $file]),
|
||||
'file_url' => \URL::signedRoute(
|
||||
'open.forms.submissions.file',
|
||||
[$this->form_id, $file],
|
||||
now()->addMinutes(10)
|
||||
),
|
||||
'file_name' => $file,
|
||||
];
|
||||
});
|
||||
|
||||
@@ -164,14 +164,14 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||
return null;
|
||||
}
|
||||
|
||||
if(filter_var($value, FILTER_VALIDATE_URL) !== FALSE && str_contains($value, parse_url(config('app.url'))['host'])) { // In case of prefill we have full url so convert to s3
|
||||
if(filter_var($value, FILTER_VALIDATE_URL) !== false && str_contains($value, parse_url(config('app.url'))['host'])) { // In case of prefill we have full url so convert to s3
|
||||
$fileName = basename($value);
|
||||
$path = FormController::ASSETS_UPLOAD_PATH . '/' . $fileName;
|
||||
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
||||
Storage::move($path, $newPath.'/'.$fileName);
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
|
||||
if($this->isSkipForUpload($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
@@ -157,12 +157,12 @@ class Form extends Model implements CachableAttributes
|
||||
if ($this->custom_domain) {
|
||||
return 'https://' . $this->custom_domain . '/forms/' . $this->slug;
|
||||
}
|
||||
return url('/forms/' . $this->slug);
|
||||
return front_url('/forms/' . $this->slug);
|
||||
}
|
||||
|
||||
public function getEditUrlAttribute()
|
||||
{
|
||||
return url('/forms/' . $this->slug . '/show');
|
||||
return front_url('/forms/' . $this->slug . '/show');
|
||||
}
|
||||
|
||||
public function getSubmissionsCountAttribute()
|
||||
|
||||
@@ -48,7 +48,7 @@ class Template extends Model
|
||||
|
||||
public function getShareUrlAttribute()
|
||||
{
|
||||
return url('/form-templates/'.$this->slug);
|
||||
return front_url('/form-templates/'.$this->slug);
|
||||
}
|
||||
|
||||
public function setDescriptionAttribute($value)
|
||||
|
||||
@@ -17,7 +17,7 @@ class ResetPassword extends Notification
|
||||
{
|
||||
return (new MailMessage)
|
||||
->line('You are receiving this email because we received a password reset request for your account.')
|
||||
->action('Reset Password', url('password/reset/'.$this->token).'?email='.urlencode($notifiable->email))
|
||||
->action('Reset Password', front_url('password/reset/'.$this->token).'?email='.urlencode($notifiable->email))
|
||||
->line('If you did not request a password reset, no further action is required.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,6 @@ class FailedPaymentNotification extends Notification implements ShouldQueue
|
||||
->line(__('Please go to OpenForm, click on your name on the top right corner, and click on "Billing".
|
||||
You will then be able to update your card details. To avoid any service disruption, you can reply to this email whenever
|
||||
you updated your card details, and we\'ll manually attempt to charge your card.'))
|
||||
->action(__('Go to OpenForm'), url('/'));
|
||||
->action(__('Go to OpenForm'), front_url('/'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,6 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public const HOME = '/home';
|
||||
|
||||
/**
|
||||
* The controller namespace for the application.
|
||||
*
|
||||
* When present, controller route declarations will automatically be prefixed with this namespace.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
// protected $namespace = 'App\\Http\\Controllers';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
@@ -39,19 +30,9 @@ class RouteServiceProvider extends ServiceProvider
|
||||
$this->registerGlobalRouteParamConstraints();
|
||||
|
||||
$this->routes(function () {
|
||||
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
Route::middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
|
||||
Route::middleware('spa')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/spa.php'));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class DiscordHandler extends AbstractWebhookHandler
|
||||
$externalLinks[] = '[**🔗 Open Form**](' . $this->form->share_url . ')';
|
||||
}
|
||||
if(Arr::get($settings, 'link_edit_form', true)){
|
||||
$editFormURL = url('forms/' . $this->form->slug . '/show');
|
||||
$editFormURL = front_url('forms/' . $this->form->slug . '/show');
|
||||
$externalLinks[] = '[**✍️ Edit Form**](' . $editFormURL . ')';
|
||||
}
|
||||
if (Arr::get($settings, 'link_edit_submission', true) && $this->form->editable_submissions) {
|
||||
|
||||
@@ -27,7 +27,7 @@ class SlackHandler extends AbstractWebhookHandler
|
||||
$externalLinks[] = '*<' . $this->form->share_url . '|🔗 Open Form>*';
|
||||
}
|
||||
if(Arr::get($settings, 'link_edit_form', true)){
|
||||
$editFormURL = url('forms/' . $this->form->slug . '/show');
|
||||
$editFormURL = front_url('forms/' . $this->form->slug . '/show');
|
||||
$externalLinks[] = '*<' . $editFormURL . '|✍️ Edit Form>*';
|
||||
}
|
||||
if (Arr::get($settings, 'link_edit_submission', true) && $this->form->editable_submissions) {
|
||||
|
||||
11
app/helpers.php
Normal file
11
app/helpers.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
function front_url($path = '')
|
||||
{
|
||||
$baseUrl = config('app.front_url');
|
||||
if (!$baseUrl) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return rtrim($baseUrl, '/'). '/' . ltrim($path, '/');
|
||||
}
|
||||
Reference in New Issue
Block a user