From 8eda76f08b09e334ead46498a36bbdafd0154e51 Mon Sep 17 00:00:00 2001 From: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:23:25 +0530 Subject: [PATCH] fix: [BACKPORT] backports buttonURL fixes in the survey editor (#7482) --- .../v1/management/surveys/[surveyId]/route.ts | 2 +- .../api/v1/management/surveys/lib/utils.ts | 48 ++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/apps/web/app/api/v1/management/surveys/[surveyId]/route.ts b/apps/web/app/api/v1/management/surveys/[surveyId]/route.ts index 025a33914e..986720865d 100644 --- a/apps/web/app/api/v1/management/surveys/[surveyId]/route.ts +++ b/apps/web/app/api/v1/management/surveys/[surveyId]/route.ts @@ -190,7 +190,7 @@ export const PUT = withV1ApiWrapper({ }; } - const featureCheckResult = await checkFeaturePermissions(surveyUpdate, organization); + const featureCheckResult = await checkFeaturePermissions(surveyUpdate, organization, result.survey); if (featureCheckResult) { return { response: featureCheckResult, diff --git a/apps/web/app/api/v1/management/surveys/lib/utils.ts b/apps/web/app/api/v1/management/surveys/lib/utils.ts index 5a982ef1f4..7337e7b3b7 100644 --- a/apps/web/app/api/v1/management/surveys/lib/utils.ts +++ b/apps/web/app/api/v1/management/surveys/lib/utils.ts @@ -1,12 +1,15 @@ import { TOrganization } from "@formbricks/types/organizations"; -import { TSurveyCreateInputWithEnvironmentId } from "@formbricks/types/surveys/types"; +import { TSurvey, TSurveyCreateInputWithEnvironmentId } from "@formbricks/types/surveys/types"; import { responses } from "@/app/lib/api/response"; +import { getElementsFromBlocks } from "@/lib/survey/utils"; import { getIsSpamProtectionEnabled } from "@/modules/ee/license-check/lib/utils"; import { getSurveyFollowUpsPermission } from "@/modules/survey/follow-ups/lib/utils"; +import { getExternalUrlsPermission } from "@/modules/survey/lib/permission"; export const checkFeaturePermissions = async ( surveyData: TSurveyCreateInputWithEnvironmentId, - organization: TOrganization + organization: TOrganization, + oldSurvey?: TSurvey ): Promise => { if (surveyData.recaptcha?.enabled) { const isSpamProtectionEnabled = await getIsSpamProtectionEnabled(organization.id); @@ -22,5 +25,46 @@ export const checkFeaturePermissions = async ( } } + const isExternalUrlsAllowed = await getExternalUrlsPermission(organization.id); + if (!isExternalUrlsAllowed) { + // Check ending cards for new/changed button links + if (surveyData.endings) { + for (const newEnding of surveyData.endings) { + const oldEnding = oldSurvey?.endings.find((e) => e.id === newEnding.id); + + if (newEnding.type === "endScreen" && newEnding.buttonLink) { + if (!oldEnding || oldEnding.type !== "endScreen" || oldEnding.buttonLink !== newEnding.buttonLink) { + return responses.forbiddenResponse( + "External URLs are not enabled for this organization. Upgrade to use external button links." + ); + } + } + } + } + + // Check CTA elements for new/changed external button URLs + if (surveyData.blocks) { + const newElements = getElementsFromBlocks(surveyData.blocks); + const oldElements = oldSurvey?.blocks ? getElementsFromBlocks(oldSurvey.blocks) : []; + + for (const newElement of newElements) { + const oldElement = oldElements.find((e) => e.id === newElement.id); + + if (newElement.type === "cta" && newElement.buttonExternal) { + if ( + !oldElement || + oldElement.type !== "cta" || + !oldElement.buttonExternal || + oldElement.buttonUrl !== newElement.buttonUrl + ) { + return responses.forbiddenResponse( + "External URLs are not enabled for this organization. Upgrade to use external CTA buttons." + ); + } + } + } + } + } + return null; };