Add cookie_policy_url field to cookie banners
Introduce a required cookie_policy_url alongside the existing
privacy_policy_url (now optional) so banners can link directly to a
dedicated cookie policy — a compliance best practice recommended by
CNIL, ICO, and the EDPB. Existing rows are seeded from their current
privacy_policy_url value.
Both {{cookie_policy_link}} and {{privacy_policy_link}} placeholders
are supported independently in banner description translations.
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -60,6 +60,7 @@ export default function NewCookieBannerPage() {
|
|||||||
|
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [origin, setOrigin] = useState("");
|
const [origin, setOrigin] = useState("");
|
||||||
|
const [cookiePolicyUrl, setCookiePolicyUrl] = useState("");
|
||||||
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState("");
|
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState("");
|
||||||
const [consentExpiryDays, setConsentExpiryDays] = useState("365");
|
const [consentExpiryDays, setConsentExpiryDays] = useState("365");
|
||||||
const [consentMode, setConsentMode] = useState("OPT_IN");
|
const [consentMode, setConsentMode] = useState("OPT_IN");
|
||||||
@@ -73,7 +74,8 @@ export default function NewCookieBannerPage() {
|
|||||||
organizationId,
|
organizationId,
|
||||||
name,
|
name,
|
||||||
origin,
|
origin,
|
||||||
privacyPolicyUrl,
|
cookiePolicyUrl,
|
||||||
|
privacyPolicyUrl: privacyPolicyUrl || undefined,
|
||||||
consentExpiryDays: parseInt(consentExpiryDays, 10),
|
consentExpiryDays: parseInt(consentExpiryDays, 10),
|
||||||
consentMode: consentMode as "OPT_IN" | "OPT_OUT",
|
consentMode: consentMode as "OPT_IN" | "OPT_OUT",
|
||||||
},
|
},
|
||||||
@@ -136,12 +138,20 @@ export default function NewCookieBannerPage() {
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field label={__("Cookie Policy URL")}>
|
||||||
|
<Input
|
||||||
|
value={cookiePolicyUrl}
|
||||||
|
onChange={e => setCookiePolicyUrl(e.target.value)}
|
||||||
|
placeholder="https://example.com/cookies"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field label={__("Privacy Policy URL")}>
|
<Field label={__("Privacy Policy URL")}>
|
||||||
<Input
|
<Input
|
||||||
value={privacyPolicyUrl}
|
value={privacyPolicyUrl}
|
||||||
onChange={e => setPrivacyPolicyUrl(e.target.value)}
|
onChange={e => setPrivacyPolicyUrl(e.target.value)}
|
||||||
placeholder="https://example.com/privacy"
|
placeholder="https://example.com/privacy"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const bannerSettingsFormFragment = graphql`
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
origin
|
origin
|
||||||
|
cookiePolicyUrl
|
||||||
privacyPolicyUrl
|
privacyPolicyUrl
|
||||||
consentExpiryDays
|
consentExpiryDays
|
||||||
consentMode
|
consentMode
|
||||||
@@ -40,6 +41,7 @@ const updateBannerMutation = graphql`
|
|||||||
cookieBanner {
|
cookieBanner {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
cookiePolicyUrl
|
||||||
privacyPolicyUrl
|
privacyPolicyUrl
|
||||||
consentExpiryDays
|
consentExpiryDays
|
||||||
consentMode
|
consentMode
|
||||||
@@ -67,7 +69,8 @@ export function BannerSettingsForm({ cookieBannerKey }: BannerSettingsFormProps)
|
|||||||
const [updateBanner, isUpdating] = useMutation<BannerSettingsFormMutation>(updateBannerMutation);
|
const [updateBanner, isUpdating] = useMutation<BannerSettingsFormMutation>(updateBannerMutation);
|
||||||
|
|
||||||
const [name, setName] = useState(banner.name);
|
const [name, setName] = useState(banner.name);
|
||||||
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState(banner.privacyPolicyUrl);
|
const [cookiePolicyUrl, setCookiePolicyUrl] = useState(banner.cookiePolicyUrl);
|
||||||
|
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState(banner.privacyPolicyUrl ?? "");
|
||||||
const [consentExpiryDays, setConsentExpiryDays] = useState(String(banner.consentExpiryDays));
|
const [consentExpiryDays, setConsentExpiryDays] = useState(String(banner.consentExpiryDays));
|
||||||
const [consentMode, setConsentMode] = useState(banner.consentMode);
|
const [consentMode, setConsentMode] = useState(banner.consentMode);
|
||||||
const [defaultLanguage, setDefaultLanguage] = useState(banner.defaultLanguage);
|
const [defaultLanguage, setDefaultLanguage] = useState(banner.defaultLanguage);
|
||||||
@@ -80,7 +83,8 @@ export function BannerSettingsForm({ cookieBannerKey }: BannerSettingsFormProps)
|
|||||||
input: {
|
input: {
|
||||||
cookieBannerId: banner.id,
|
cookieBannerId: banner.id,
|
||||||
name,
|
name,
|
||||||
privacyPolicyUrl,
|
cookiePolicyUrl,
|
||||||
|
privacyPolicyUrl: privacyPolicyUrl || undefined,
|
||||||
consentExpiryDays: parseInt(consentExpiryDays, 10),
|
consentExpiryDays: parseInt(consentExpiryDays, 10),
|
||||||
consentMode: consentMode,
|
consentMode: consentMode,
|
||||||
defaultLanguage,
|
defaultLanguage,
|
||||||
@@ -108,8 +112,12 @@ export function BannerSettingsForm({ cookieBannerKey }: BannerSettingsFormProps)
|
|||||||
<Input value={banner.origin} disabled />
|
<Input value={banner.origin} disabled />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field label={__("Cookie Policy URL")}>
|
||||||
|
<Input value={cookiePolicyUrl} onChange={e => setCookiePolicyUrl(e.target.value)} required />
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field label={__("Privacy Policy URL")}>
|
<Field label={__("Privacy Policy URL")}>
|
||||||
<Input value={privacyPolicyUrl} onChange={e => setPrivacyPolicyUrl(e.target.value)} required />
|
<Input value={privacyPolicyUrl} onChange={e => setPrivacyPolicyUrl(e.target.value)} />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
|||||||
@@ -21,19 +21,10 @@ interface BannerPreviewProps {
|
|||||||
buttonRejectAll: string;
|
buttonRejectAll: string;
|
||||||
buttonCustomize: string;
|
buttonCustomize: string;
|
||||||
privacyPolicyLinkText: string;
|
privacyPolicyLinkText: string;
|
||||||
|
cookiePolicyLinkText: string;
|
||||||
showBranding: boolean;
|
showBranding: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function interpolateDescription(
|
|
||||||
description: string,
|
|
||||||
linkText: string,
|
|
||||||
): string {
|
|
||||||
return description.replaceAll(
|
|
||||||
"{{privacy_policy_link}}",
|
|
||||||
linkText,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BannerPreview({
|
export function BannerPreview({
|
||||||
bannerTitle,
|
bannerTitle,
|
||||||
bannerDescription,
|
bannerDescription,
|
||||||
@@ -41,10 +32,11 @@ export function BannerPreview({
|
|||||||
buttonRejectAll,
|
buttonRejectAll,
|
||||||
buttonCustomize,
|
buttonCustomize,
|
||||||
privacyPolicyLinkText,
|
privacyPolicyLinkText,
|
||||||
|
cookiePolicyLinkText,
|
||||||
showBranding,
|
showBranding,
|
||||||
}: BannerPreviewProps) {
|
}: BannerPreviewProps) {
|
||||||
const descriptionParts = bannerDescription.split("{{privacy_policy_link}}");
|
const cookieParts = bannerDescription.split("{{cookie_policy_link}}");
|
||||||
const hasPlaceholder = descriptionParts.length > 1;
|
const hasAnyPlaceholder = cookieParts.length > 1 || bannerDescription.includes("{{privacy_policy_link}}");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -78,27 +70,27 @@ export function BannerPreview({
|
|||||||
margin: "0 0 20px",
|
margin: "0 0 20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{hasPlaceholder
|
{hasAnyPlaceholder
|
||||||
? descriptionParts.map((part, i) => (
|
? cookieParts.map((cookiePart, ci) => (
|
||||||
<span key={i}>
|
<span key={`c${ci}`}>
|
||||||
{part}
|
{cookiePart.split("{{privacy_policy_link}}").map((privPart, pi, arr) => (
|
||||||
{i < descriptionParts.length - 1 && (
|
<span key={`p${pi}`}>
|
||||||
<a
|
{privPart}
|
||||||
href="#"
|
{pi < arr.length - 1 && (
|
||||||
onClick={e => e.preventDefault()}
|
<a href="#" onClick={e => e.preventDefault()} style={{ color: "var(--probo-accent, #1a1a1a)", textDecoration: "underline" }}>
|
||||||
style={{
|
{privacyPolicyLinkText}
|
||||||
color: "var(--probo-accent, #1a1a1a)",
|
</a>
|
||||||
textDecoration: "underline",
|
)}
|
||||||
}}
|
</span>
|
||||||
>
|
))}
|
||||||
{privacyPolicyLinkText}
|
{ci < cookieParts.length - 1 && (
|
||||||
|
<a href="#" onClick={e => e.preventDefault()} style={{ color: "var(--probo-accent, #1a1a1a)", textDecoration: "underline" }}>
|
||||||
|
{cookiePolicyLinkText}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
))
|
))
|
||||||
: (
|
: bannerDescription}
|
||||||
interpolateDescription(bannerDescription, privacyPolicyLinkText)
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ export function BannerTranslationSection({
|
|||||||
const buttonAcceptAll = useWatch({ control, name: "button_accept_all" });
|
const buttonAcceptAll = useWatch({ control, name: "button_accept_all" });
|
||||||
const buttonRejectAll = useWatch({ control, name: "button_reject_all" });
|
const buttonRejectAll = useWatch({ control, name: "button_reject_all" });
|
||||||
const buttonCustomize = useWatch({ control, name: "button_customize" });
|
const buttonCustomize = useWatch({ control, name: "button_customize" });
|
||||||
|
const cookiePolicyLinkText = useWatch({
|
||||||
|
control,
|
||||||
|
name: "cookie_policy_link_text",
|
||||||
|
});
|
||||||
const privacyPolicyLinkText = useWatch({
|
const privacyPolicyLinkText = useWatch({
|
||||||
control,
|
control,
|
||||||
name: "privacy_policy_link_text",
|
name: "privacy_policy_link_text",
|
||||||
@@ -62,7 +66,7 @@ export function BannerTranslationSection({
|
|||||||
<Field
|
<Field
|
||||||
label={__(TRANSLATION_LABELS.banner_description)}
|
label={__(TRANSLATION_LABELS.banner_description)}
|
||||||
>
|
>
|
||||||
<p className="text-xs text-txt-secondary mb-2">{"Use {{privacy_policy_link}} to insert the privacy policy link."}</p>
|
<p className="text-xs text-txt-secondary mb-2">{"Use {{cookie_policy_link}} and {{privacy_policy_link}} to insert policy links."}</p>
|
||||||
<Textarea {...field} rows={3} />
|
<Textarea {...field} rows={3} />
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
@@ -87,12 +91,23 @@ export function BannerTranslationSection({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="button_customize"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Field label={__(TRANSLATION_LABELS.button_customize)}>
|
||||||
|
<Input {...field} />
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="button_customize"
|
name="cookie_policy_link_text"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Field label={__(TRANSLATION_LABELS.button_customize)}>
|
<Field
|
||||||
|
label={__(TRANSLATION_LABELS.cookie_policy_link_text)}
|
||||||
|
>
|
||||||
<Input {...field} />
|
<Input {...field} />
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
@@ -119,6 +134,7 @@ export function BannerTranslationSection({
|
|||||||
buttonAcceptAll={buttonAcceptAll}
|
buttonAcceptAll={buttonAcceptAll}
|
||||||
buttonRejectAll={buttonRejectAll}
|
buttonRejectAll={buttonRejectAll}
|
||||||
buttonCustomize={buttonCustomize}
|
buttonCustomize={buttonCustomize}
|
||||||
|
cookiePolicyLinkText={cookiePolicyLinkText}
|
||||||
privacyPolicyLinkText={privacyPolicyLinkText}
|
privacyPolicyLinkText={privacyPolicyLinkText}
|
||||||
showBranding={showBranding}
|
showBranding={showBranding}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const BANNER_KEYS = [
|
|||||||
"button_accept_all",
|
"button_accept_all",
|
||||||
"button_reject_all",
|
"button_reject_all",
|
||||||
"button_customize",
|
"button_customize",
|
||||||
|
"cookie_policy_link_text",
|
||||||
"privacy_policy_link_text",
|
"privacy_policy_link_text",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ export const TRANSLATION_LABELS: Record<string, string> = {
|
|||||||
button_accept_all: "Accept all button",
|
button_accept_all: "Accept all button",
|
||||||
button_reject_all: "Reject all button",
|
button_reject_all: "Reject all button",
|
||||||
button_customize: "Customize button",
|
button_customize: "Customize button",
|
||||||
|
cookie_policy_link_text: "Cookie policy link text",
|
||||||
privacy_policy_link_text: "Privacy policy link text",
|
privacy_policy_link_text: "Privacy policy link text",
|
||||||
panel_title: "Panel title",
|
panel_title: "Panel title",
|
||||||
panel_description: "Panel description",
|
panel_description: "Panel description",
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ export interface BannerConfig {
|
|||||||
version: number;
|
version: number;
|
||||||
language: string;
|
language: string;
|
||||||
default_language: string;
|
default_language: string;
|
||||||
privacy_policy_url: string;
|
privacy_policy_url?: string;
|
||||||
|
cookie_policy_url: string;
|
||||||
consent_expiry_days: number;
|
consent_expiry_days: number;
|
||||||
consent_mode: "OPT_IN" | "OPT_OUT";
|
consent_mode: "OPT_IN" | "OPT_OUT";
|
||||||
show_branding: boolean;
|
show_branding: boolean;
|
||||||
|
|||||||
@@ -187,13 +187,21 @@ export class ProboThemedBanner extends HTMLElement {
|
|||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
|
|
||||||
if (key === "banner_description") {
|
if (key === "banner_description") {
|
||||||
let link = "";
|
let privacyLink = "";
|
||||||
if (config.privacy_policy_url) {
|
if (config.privacy_policy_url) {
|
||||||
const linkText = this.esc(texts.privacy_policy_link_text ?? "Privacy Policy");
|
const linkText = this.esc(texts.privacy_policy_link_text ?? "Privacy Policy");
|
||||||
link = `<a href="${this.esc(config.privacy_policy_url)}" target="_blank" rel="noopener noreferrer">${linkText}</a>`;
|
privacyLink = `<a href="${this.esc(config.privacy_policy_url)}" target="_blank" rel="noopener noreferrer">${linkText}</a>`;
|
||||||
}
|
}
|
||||||
const parts = raw.split("{{privacy_policy_link}}");
|
let cookieLink = "";
|
||||||
el.innerHTML = parts.map(p => this.esc(p)).join(link);
|
if (config.cookie_policy_url) {
|
||||||
|
const linkText = this.esc(texts.cookie_policy_link_text ?? "Cookie Policy");
|
||||||
|
cookieLink = `<a href="${this.esc(config.cookie_policy_url)}" target="_blank" rel="noopener noreferrer">${linkText}</a>`;
|
||||||
|
}
|
||||||
|
const segments = raw.split("{{cookie_policy_link}}");
|
||||||
|
const html = segments.map(seg =>
|
||||||
|
seg.split("{{privacy_policy_link}}").map(p => this.esc(p)).join(privacyLink),
|
||||||
|
).join(cookieLink);
|
||||||
|
el.innerHTML = html;
|
||||||
} else if (key === "panel_description") {
|
} else if (key === "panel_description") {
|
||||||
el.textContent = interpolate(raw, { necessary_category: necessaryCategoryName });
|
el.textContent = interpolate(raw, { necessary_category: necessaryCategoryName });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ var defaultCategoryTranslationsByLanguage = map[string]map[string]struct {
|
|||||||
var defaultUIStringsByLanguage = map[string]map[string]string{
|
var defaultUIStringsByLanguage = map[string]map[string]string{
|
||||||
"en": {
|
"en": {
|
||||||
"banner_title": "Cookie Preferences",
|
"banner_title": "Cookie Preferences",
|
||||||
"banner_description": "We use cookies to improve your experience and analyze site traffic. {{privacy_policy_link}}",
|
"banner_description": "We use cookies to improve your experience and analyze site traffic. {{cookie_policy_link}}",
|
||||||
"button_accept_all": "Accept all",
|
"button_accept_all": "Accept all",
|
||||||
"button_reject_all": "Reject all",
|
"button_reject_all": "Reject all",
|
||||||
"button_customize": "Customize",
|
"button_customize": "Customize",
|
||||||
@@ -116,12 +116,13 @@ var defaultUIStringsByLanguage = map[string]map[string]string{
|
|||||||
"aria_hide_details": "Hide cookie details",
|
"aria_hide_details": "Hide cookie details",
|
||||||
"aria_cookie_settings": "Cookie settings",
|
"aria_cookie_settings": "Cookie settings",
|
||||||
"privacy_policy_link_text": "Privacy Policy",
|
"privacy_policy_link_text": "Privacy Policy",
|
||||||
|
"cookie_policy_link_text": "Cookie Policy",
|
||||||
"placeholder_text": "This content requires {{category}} cookies.",
|
"placeholder_text": "This content requires {{category}} cookies.",
|
||||||
"placeholder_button": "Manage cookie preferences",
|
"placeholder_button": "Manage cookie preferences",
|
||||||
},
|
},
|
||||||
"fr": {
|
"fr": {
|
||||||
"banner_title": "Préférences de cookies",
|
"banner_title": "Préférences de cookies",
|
||||||
"banner_description": "Nous utilisons des cookies pour améliorer votre expérience et analyser le trafic du site. {{privacy_policy_link}}",
|
"banner_description": "Nous utilisons des cookies pour améliorer votre expérience et analyser le trafic du site. {{cookie_policy_link}}",
|
||||||
"button_accept_all": "Tout accepter",
|
"button_accept_all": "Tout accepter",
|
||||||
"button_reject_all": "Tout refuser",
|
"button_reject_all": "Tout refuser",
|
||||||
"button_customize": "Personnaliser",
|
"button_customize": "Personnaliser",
|
||||||
@@ -133,12 +134,13 @@ var defaultUIStringsByLanguage = map[string]map[string]string{
|
|||||||
"aria_hide_details": "Masquer les détails des cookies",
|
"aria_hide_details": "Masquer les détails des cookies",
|
||||||
"aria_cookie_settings": "Paramètres des cookies",
|
"aria_cookie_settings": "Paramètres des cookies",
|
||||||
"privacy_policy_link_text": "Politique de confidentialité",
|
"privacy_policy_link_text": "Politique de confidentialité",
|
||||||
|
"cookie_policy_link_text": "Politique relative aux cookies",
|
||||||
"placeholder_text": "Ce contenu nécessite les cookies {{category}}.",
|
"placeholder_text": "Ce contenu nécessite les cookies {{category}}.",
|
||||||
"placeholder_button": "Gérer les préférences de cookies",
|
"placeholder_button": "Gérer les préférences de cookies",
|
||||||
},
|
},
|
||||||
"de": {
|
"de": {
|
||||||
"banner_title": "Cookie-Einstellungen",
|
"banner_title": "Cookie-Einstellungen",
|
||||||
"banner_description": "Wir verwenden Cookies, um Ihre Erfahrung zu verbessern und den Website-Verkehr zu analysieren. {{privacy_policy_link}}",
|
"banner_description": "Wir verwenden Cookies, um Ihre Erfahrung zu verbessern und den Website-Verkehr zu analysieren. {{cookie_policy_link}}",
|
||||||
"button_accept_all": "Alle akzeptieren",
|
"button_accept_all": "Alle akzeptieren",
|
||||||
"button_reject_all": "Alle ablehnen",
|
"button_reject_all": "Alle ablehnen",
|
||||||
"button_customize": "Anpassen",
|
"button_customize": "Anpassen",
|
||||||
@@ -150,12 +152,13 @@ var defaultUIStringsByLanguage = map[string]map[string]string{
|
|||||||
"aria_hide_details": "Cookie-Details ausblenden",
|
"aria_hide_details": "Cookie-Details ausblenden",
|
||||||
"aria_cookie_settings": "Cookie-Einstellungen",
|
"aria_cookie_settings": "Cookie-Einstellungen",
|
||||||
"privacy_policy_link_text": "Datenschutzrichtlinie",
|
"privacy_policy_link_text": "Datenschutzrichtlinie",
|
||||||
|
"cookie_policy_link_text": "Cookie-Richtlinie",
|
||||||
"placeholder_text": "Dieser Inhalt erfordert {{category}}-Cookies.",
|
"placeholder_text": "Dieser Inhalt erfordert {{category}}-Cookies.",
|
||||||
"placeholder_button": "Cookie-Einstellungen verwalten",
|
"placeholder_button": "Cookie-Einstellungen verwalten",
|
||||||
},
|
},
|
||||||
"es": {
|
"es": {
|
||||||
"banner_title": "Preferencias de cookies",
|
"banner_title": "Preferencias de cookies",
|
||||||
"banner_description": "Utilizamos cookies para mejorar su experiencia y analizar el tráfico del sitio. {{privacy_policy_link}}",
|
"banner_description": "Utilizamos cookies para mejorar su experiencia y analizar el tráfico del sitio. {{cookie_policy_link}}",
|
||||||
"button_accept_all": "Aceptar todo",
|
"button_accept_all": "Aceptar todo",
|
||||||
"button_reject_all": "Rechazar todo",
|
"button_reject_all": "Rechazar todo",
|
||||||
"button_customize": "Personalizar",
|
"button_customize": "Personalizar",
|
||||||
@@ -167,6 +170,7 @@ var defaultUIStringsByLanguage = map[string]map[string]string{
|
|||||||
"aria_hide_details": "Ocultar detalles de cookies",
|
"aria_hide_details": "Ocultar detalles de cookies",
|
||||||
"aria_cookie_settings": "Configuración de cookies",
|
"aria_cookie_settings": "Configuración de cookies",
|
||||||
"privacy_policy_link_text": "Política de privacidad",
|
"privacy_policy_link_text": "Política de privacidad",
|
||||||
|
"cookie_policy_link_text": "Política de cookies",
|
||||||
"placeholder_text": "Este contenido requiere cookies de {{category}}.",
|
"placeholder_text": "Este contenido requiere cookies de {{category}}.",
|
||||||
"placeholder_button": "Gestionar preferencias de cookies",
|
"placeholder_button": "Gestionar preferencias de cookies",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ type (
|
|||||||
OrganizationID gid.GID
|
OrganizationID gid.GID
|
||||||
Name string
|
Name string
|
||||||
Origin string
|
Origin string
|
||||||
PrivacyPolicyURL string
|
PrivacyPolicyURL *string
|
||||||
|
CookiePolicyURL string
|
||||||
ConsentExpiryDays int
|
ConsentExpiryDays int
|
||||||
ConsentMode coredata.CookieConsentMode
|
ConsentMode coredata.CookieConsentMode
|
||||||
}
|
}
|
||||||
@@ -62,6 +63,7 @@ type (
|
|||||||
CookieBannerID gid.GID
|
CookieBannerID gid.GID
|
||||||
Name *string
|
Name *string
|
||||||
PrivacyPolicyURL *string
|
PrivacyPolicyURL *string
|
||||||
|
CookiePolicyURL *string
|
||||||
ConsentExpiryDays *int
|
ConsentExpiryDays *int
|
||||||
ConsentMode *coredata.CookieConsentMode
|
ConsentMode *coredata.CookieConsentMode
|
||||||
DefaultLanguage *string
|
DefaultLanguage *string
|
||||||
@@ -133,7 +135,8 @@ type (
|
|||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
DefaultLanguage string `json:"default_language"`
|
DefaultLanguage string `json:"default_language"`
|
||||||
PrivacyPolicyURL string `json:"privacy_policy_url"`
|
PrivacyPolicyURL string `json:"privacy_policy_url,omitempty"`
|
||||||
|
CookiePolicyURL string `json:"cookie_policy_url"`
|
||||||
ConsentExpiryDays int `json:"consent_expiry_days"`
|
ConsentExpiryDays int `json:"consent_expiry_days"`
|
||||||
ConsentMode string `json:"consent_mode"`
|
ConsentMode string `json:"consent_mode"`
|
||||||
ShowBranding bool `json:"show_branding"`
|
ShowBranding bool `json:"show_branding"`
|
||||||
@@ -162,7 +165,8 @@ func (r *CreateCookieBannerRequest) Validate() error {
|
|||||||
v.Check(r.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
|
v.Check(r.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
|
||||||
v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(255))
|
v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(255))
|
||||||
v.Check(r.Origin, "origin", validator.Required(), validator.Origin())
|
v.Check(r.Origin, "origin", validator.Required(), validator.Origin())
|
||||||
v.Check(r.PrivacyPolicyURL, "privacy_policy_url", validator.Required(), validator.URL())
|
v.Check(r.PrivacyPolicyURL, "privacy_policy_url", validator.URL())
|
||||||
|
v.Check(r.CookiePolicyURL, "cookie_policy_url", validator.Required(), validator.URL())
|
||||||
v.Check(r.ConsentExpiryDays, "consent_expiry_days", validator.Required(), validator.Min(1))
|
v.Check(r.ConsentExpiryDays, "consent_expiry_days", validator.Required(), validator.Min(1))
|
||||||
v.Check(r.ConsentMode, "consent_mode", validator.Required(), validator.OneOfSlice(coredata.CookieConsentModes()))
|
v.Check(r.ConsentMode, "consent_mode", validator.Required(), validator.OneOfSlice(coredata.CookieConsentModes()))
|
||||||
|
|
||||||
@@ -175,6 +179,7 @@ func (r *UpdateCookieBannerRequest) Validate() error {
|
|||||||
v.Check(r.CookieBannerID, "cookie_banner_id", validator.Required(), validator.GID(coredata.CookieBannerEntityType))
|
v.Check(r.CookieBannerID, "cookie_banner_id", validator.Required(), validator.GID(coredata.CookieBannerEntityType))
|
||||||
v.Check(r.Name, "name", validator.SafeTextNoNewLine(255))
|
v.Check(r.Name, "name", validator.SafeTextNoNewLine(255))
|
||||||
v.Check(r.PrivacyPolicyURL, "privacy_policy_url", validator.URL())
|
v.Check(r.PrivacyPolicyURL, "privacy_policy_url", validator.URL())
|
||||||
|
v.Check(r.CookiePolicyURL, "cookie_policy_url", validator.URL())
|
||||||
v.Check(r.ConsentExpiryDays, "consent_expiry_days", validator.Min(1))
|
v.Check(r.ConsentExpiryDays, "consent_expiry_days", validator.Min(1))
|
||||||
v.Check(r.ConsentMode, "consent_mode", validator.OneOfSlice(coredata.CookieConsentModes()))
|
v.Check(r.ConsentMode, "consent_mode", validator.OneOfSlice(coredata.CookieConsentModes()))
|
||||||
v.Check(r.DefaultLanguage, "default_language", validator.OneOfSlice(SupportedLanguages))
|
v.Check(r.DefaultLanguage, "default_language", validator.OneOfSlice(SupportedLanguages))
|
||||||
@@ -367,6 +372,7 @@ func buildSnapshot(
|
|||||||
|
|
||||||
return coredata.CookieBannerVersionSnapshot{
|
return coredata.CookieBannerVersionSnapshot{
|
||||||
PrivacyPolicyURL: banner.PrivacyPolicyURL,
|
PrivacyPolicyURL: banner.PrivacyPolicyURL,
|
||||||
|
CookiePolicyURL: banner.CookiePolicyURL,
|
||||||
ConsentExpiryDays: banner.ConsentExpiryDays,
|
ConsentExpiryDays: banner.ConsentExpiryDays,
|
||||||
ConsentMode: string(banner.ConsentMode),
|
ConsentMode: string(banner.ConsentMode),
|
||||||
DefaultLanguage: banner.DefaultLanguage,
|
DefaultLanguage: banner.DefaultLanguage,
|
||||||
@@ -541,6 +547,7 @@ func (s *Service) CreateCookieBanner(
|
|||||||
Origin: CanonicalizeOrigin(req.Origin),
|
Origin: CanonicalizeOrigin(req.Origin),
|
||||||
State: coredata.CookieBannerStateActive,
|
State: coredata.CookieBannerStateActive,
|
||||||
PrivacyPolicyURL: req.PrivacyPolicyURL,
|
PrivacyPolicyURL: req.PrivacyPolicyURL,
|
||||||
|
CookiePolicyURL: req.CookiePolicyURL,
|
||||||
ConsentExpiryDays: req.ConsentExpiryDays,
|
ConsentExpiryDays: req.ConsentExpiryDays,
|
||||||
ConsentMode: req.ConsentMode,
|
ConsentMode: req.ConsentMode,
|
||||||
ShowBranding: s.showBranding,
|
ShowBranding: s.showBranding,
|
||||||
@@ -768,13 +775,20 @@ func (s *Service) UpdateCookieBanner(
|
|||||||
return fmt.Errorf("cannot load cookie banner: %w", err)
|
return fmt.Errorf("cannot load cookie banner: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
consentChanged := req.PrivacyPolicyURL != nil || req.ConsentExpiryDays != nil || req.ConsentMode != nil || req.DefaultLanguage != nil
|
consentChanged := req.PrivacyPolicyURL != nil ||
|
||||||
|
req.CookiePolicyURL != nil ||
|
||||||
|
req.ConsentExpiryDays != nil ||
|
||||||
|
req.ConsentMode != nil ||
|
||||||
|
req.DefaultLanguage != nil
|
||||||
|
|
||||||
if req.Name != nil {
|
if req.Name != nil {
|
||||||
banner.Name = *req.Name
|
banner.Name = *req.Name
|
||||||
}
|
}
|
||||||
if req.PrivacyPolicyURL != nil {
|
if req.PrivacyPolicyURL != nil {
|
||||||
banner.PrivacyPolicyURL = *req.PrivacyPolicyURL
|
banner.PrivacyPolicyURL = req.PrivacyPolicyURL
|
||||||
|
}
|
||||||
|
if req.CookiePolicyURL != nil {
|
||||||
|
banner.CookiePolicyURL = *req.CookiePolicyURL
|
||||||
}
|
}
|
||||||
if req.ConsentExpiryDays != nil {
|
if req.ConsentExpiryDays != nil {
|
||||||
banner.ConsentExpiryDays = *req.ConsentExpiryDays
|
banner.ConsentExpiryDays = *req.ConsentExpiryDays
|
||||||
@@ -1817,12 +1831,18 @@ func buildBannerConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var privacyPolicyURL string
|
||||||
|
if snapshot.PrivacyPolicyURL != nil {
|
||||||
|
privacyPolicyURL = *snapshot.PrivacyPolicyURL
|
||||||
|
}
|
||||||
|
|
||||||
return &BannerConfig{
|
return &BannerConfig{
|
||||||
BannerID: banner.ID,
|
BannerID: banner.ID,
|
||||||
Version: version.Version,
|
Version: version.Version,
|
||||||
Language: resolvedLang,
|
Language: resolvedLang,
|
||||||
DefaultLanguage: defaultLang,
|
DefaultLanguage: defaultLang,
|
||||||
PrivacyPolicyURL: snapshot.PrivacyPolicyURL,
|
PrivacyPolicyURL: privacyPolicyURL,
|
||||||
|
CookiePolicyURL: snapshot.CookiePolicyURL,
|
||||||
ConsentExpiryDays: snapshot.ConsentExpiryDays,
|
ConsentExpiryDays: snapshot.ConsentExpiryDays,
|
||||||
ConsentMode: snapshot.ConsentMode,
|
ConsentMode: snapshot.ConsentMode,
|
||||||
ShowBranding: banner.ShowBranding,
|
ShowBranding: banner.ShowBranding,
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ type (
|
|||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
Origin string `db:"origin"`
|
Origin string `db:"origin"`
|
||||||
State CookieBannerState `db:"state"`
|
State CookieBannerState `db:"state"`
|
||||||
PrivacyPolicyURL string `db:"privacy_policy_url"`
|
PrivacyPolicyURL *string `db:"privacy_policy_url"`
|
||||||
|
CookiePolicyURL string `db:"cookie_policy_url"`
|
||||||
ConsentExpiryDays int `db:"consent_expiry_days"`
|
ConsentExpiryDays int `db:"consent_expiry_days"`
|
||||||
ConsentMode CookieConsentMode `db:"consent_mode"`
|
ConsentMode CookieConsentMode `db:"consent_mode"`
|
||||||
ShowBranding bool `db:"show_branding"`
|
ShowBranding bool `db:"show_branding"`
|
||||||
@@ -85,6 +86,7 @@ SELECT
|
|||||||
origin,
|
origin,
|
||||||
state,
|
state,
|
||||||
privacy_policy_url,
|
privacy_policy_url,
|
||||||
|
cookie_policy_url,
|
||||||
consent_expiry_days,
|
consent_expiry_days,
|
||||||
consent_mode,
|
consent_mode,
|
||||||
show_branding,
|
show_branding,
|
||||||
@@ -136,6 +138,7 @@ SELECT
|
|||||||
origin,
|
origin,
|
||||||
state,
|
state,
|
||||||
privacy_policy_url,
|
privacy_policy_url,
|
||||||
|
cookie_policy_url,
|
||||||
consent_expiry_days,
|
consent_expiry_days,
|
||||||
consent_mode,
|
consent_mode,
|
||||||
show_branding,
|
show_branding,
|
||||||
@@ -188,6 +191,7 @@ SELECT
|
|||||||
origin,
|
origin,
|
||||||
state,
|
state,
|
||||||
privacy_policy_url,
|
privacy_policy_url,
|
||||||
|
cookie_policy_url,
|
||||||
consent_expiry_days,
|
consent_expiry_days,
|
||||||
consent_mode,
|
consent_mode,
|
||||||
show_branding,
|
show_branding,
|
||||||
@@ -246,6 +250,7 @@ SELECT
|
|||||||
origin,
|
origin,
|
||||||
state,
|
state,
|
||||||
privacy_policy_url,
|
privacy_policy_url,
|
||||||
|
cookie_policy_url,
|
||||||
consent_expiry_days,
|
consent_expiry_days,
|
||||||
consent_mode,
|
consent_mode,
|
||||||
show_branding,
|
show_branding,
|
||||||
@@ -331,6 +336,7 @@ INSERT INTO cookie_banners (
|
|||||||
origin,
|
origin,
|
||||||
state,
|
state,
|
||||||
privacy_policy_url,
|
privacy_policy_url,
|
||||||
|
cookie_policy_url,
|
||||||
consent_expiry_days,
|
consent_expiry_days,
|
||||||
consent_mode,
|
consent_mode,
|
||||||
show_branding,
|
show_branding,
|
||||||
@@ -345,6 +351,7 @@ INSERT INTO cookie_banners (
|
|||||||
@origin,
|
@origin,
|
||||||
@state,
|
@state,
|
||||||
@privacy_policy_url,
|
@privacy_policy_url,
|
||||||
|
@cookie_policy_url,
|
||||||
@consent_expiry_days,
|
@consent_expiry_days,
|
||||||
@consent_mode,
|
@consent_mode,
|
||||||
@show_branding,
|
@show_branding,
|
||||||
@@ -362,6 +369,7 @@ INSERT INTO cookie_banners (
|
|||||||
"origin": b.Origin,
|
"origin": b.Origin,
|
||||||
"state": b.State,
|
"state": b.State,
|
||||||
"privacy_policy_url": b.PrivacyPolicyURL,
|
"privacy_policy_url": b.PrivacyPolicyURL,
|
||||||
|
"cookie_policy_url": b.CookiePolicyURL,
|
||||||
"consent_expiry_days": b.ConsentExpiryDays,
|
"consent_expiry_days": b.ConsentExpiryDays,
|
||||||
"consent_mode": b.ConsentMode,
|
"consent_mode": b.ConsentMode,
|
||||||
"show_branding": b.ShowBranding,
|
"show_branding": b.ShowBranding,
|
||||||
@@ -394,6 +402,7 @@ SET
|
|||||||
name = @name,
|
name = @name,
|
||||||
state = @state,
|
state = @state,
|
||||||
privacy_policy_url = @privacy_policy_url,
|
privacy_policy_url = @privacy_policy_url,
|
||||||
|
cookie_policy_url = @cookie_policy_url,
|
||||||
consent_expiry_days = @consent_expiry_days,
|
consent_expiry_days = @consent_expiry_days,
|
||||||
consent_mode = @consent_mode,
|
consent_mode = @consent_mode,
|
||||||
show_branding = @show_branding,
|
show_branding = @show_branding,
|
||||||
@@ -411,6 +420,7 @@ WHERE
|
|||||||
"name": b.Name,
|
"name": b.Name,
|
||||||
"state": b.State,
|
"state": b.State,
|
||||||
"privacy_policy_url": b.PrivacyPolicyURL,
|
"privacy_policy_url": b.PrivacyPolicyURL,
|
||||||
|
"cookie_policy_url": b.CookiePolicyURL,
|
||||||
"consent_expiry_days": b.ConsentExpiryDays,
|
"consent_expiry_days": b.ConsentExpiryDays,
|
||||||
"consent_mode": b.ConsentMode,
|
"consent_mode": b.ConsentMode,
|
||||||
"show_branding": b.ShowBranding,
|
"show_branding": b.ShowBranding,
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
CookieBannerVersionSnapshot struct {
|
CookieBannerVersionSnapshot struct {
|
||||||
PrivacyPolicyURL string `json:"privacy_policy_url"`
|
PrivacyPolicyURL *string `json:"privacy_policy_url,omitempty"`
|
||||||
|
CookiePolicyURL string `json:"cookie_policy_url"`
|
||||||
ConsentExpiryDays int `json:"consent_expiry_days"`
|
ConsentExpiryDays int `json:"consent_expiry_days"`
|
||||||
ConsentMode string `json:"consent_mode"`
|
ConsentMode string `json:"consent_mode"`
|
||||||
DefaultLanguage string `json:"default_language"`
|
DefaultLanguage string `json:"default_language"`
|
||||||
|
|||||||
8
pkg/coredata/migrations/20260424T140000Z.sql
Normal file
8
pkg/coredata/migrations/20260424T140000Z.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Add cookie_policy_url (required) and make privacy_policy_url optional.
|
||||||
|
-- Seed cookie_policy_url from existing privacy_policy_url for data continuity.
|
||||||
|
|
||||||
|
ALTER TABLE cookie_banners ADD COLUMN cookie_policy_url TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
UPDATE cookie_banners SET cookie_policy_url = privacy_policy_url WHERE cookie_policy_url = '';
|
||||||
|
|
||||||
|
ALTER TABLE cookie_banners ALTER COLUMN privacy_policy_url DROP NOT NULL;
|
||||||
@@ -235,6 +235,7 @@ func (r *mutationResolver) CreateCookieBanner(ctx context.Context, input types.C
|
|||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
Origin: input.Origin,
|
Origin: input.Origin,
|
||||||
PrivacyPolicyURL: input.PrivacyPolicyURL,
|
PrivacyPolicyURL: input.PrivacyPolicyURL,
|
||||||
|
CookiePolicyURL: input.CookiePolicyURL,
|
||||||
ConsentExpiryDays: input.ConsentExpiryDays,
|
ConsentExpiryDays: input.ConsentExpiryDays,
|
||||||
ConsentMode: input.ConsentMode,
|
ConsentMode: input.ConsentMode,
|
||||||
},
|
},
|
||||||
@@ -270,6 +271,7 @@ func (r *mutationResolver) UpdateCookieBanner(ctx context.Context, input types.U
|
|||||||
CookieBannerID: input.CookieBannerID,
|
CookieBannerID: input.CookieBannerID,
|
||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
PrivacyPolicyURL: input.PrivacyPolicyURL,
|
PrivacyPolicyURL: input.PrivacyPolicyURL,
|
||||||
|
CookiePolicyURL: input.CookiePolicyURL,
|
||||||
ConsentExpiryDays: input.ConsentExpiryDays,
|
ConsentExpiryDays: input.ConsentExpiryDays,
|
||||||
ConsentMode: input.ConsentMode,
|
ConsentMode: input.ConsentMode,
|
||||||
DefaultLanguage: input.DefaultLanguage,
|
DefaultLanguage: input.DefaultLanguage,
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ type CookieBanner implements Node {
|
|||||||
name: String!
|
name: String!
|
||||||
origin: String!
|
origin: String!
|
||||||
state: CookieBannerState!
|
state: CookieBannerState!
|
||||||
privacyPolicyUrl: String!
|
privacyPolicyUrl: String
|
||||||
|
cookiePolicyUrl: String!
|
||||||
consentExpiryDays: Int!
|
consentExpiryDays: Int!
|
||||||
consentMode: CookieConsentMode!
|
consentMode: CookieConsentMode!
|
||||||
showBranding: Boolean!
|
showBranding: Boolean!
|
||||||
@@ -266,7 +267,8 @@ input CreateCookieBannerInput {
|
|||||||
organizationId: ID!
|
organizationId: ID!
|
||||||
name: String!
|
name: String!
|
||||||
origin: String!
|
origin: String!
|
||||||
privacyPolicyUrl: String!
|
privacyPolicyUrl: String
|
||||||
|
cookiePolicyUrl: String!
|
||||||
consentExpiryDays: Int!
|
consentExpiryDays: Int!
|
||||||
consentMode: CookieConsentMode!
|
consentMode: CookieConsentMode!
|
||||||
}
|
}
|
||||||
@@ -275,6 +277,7 @@ input UpdateCookieBannerInput {
|
|||||||
cookieBannerId: ID!
|
cookieBannerId: ID!
|
||||||
name: String
|
name: String
|
||||||
privacyPolicyUrl: String
|
privacyPolicyUrl: String
|
||||||
|
cookiePolicyUrl: String
|
||||||
consentExpiryDays: Int
|
consentExpiryDays: Int
|
||||||
consentMode: CookieConsentMode
|
consentMode: CookieConsentMode
|
||||||
defaultLanguage: String
|
defaultLanguage: String
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ func NewCookieBanner(b *coredata.CookieBanner) *CookieBanner {
|
|||||||
Origin: b.Origin,
|
Origin: b.Origin,
|
||||||
State: b.State,
|
State: b.State,
|
||||||
PrivacyPolicyURL: b.PrivacyPolicyURL,
|
PrivacyPolicyURL: b.PrivacyPolicyURL,
|
||||||
|
CookiePolicyURL: b.CookiePolicyURL,
|
||||||
ConsentExpiryDays: b.ConsentExpiryDays,
|
ConsentExpiryDays: b.ConsentExpiryDays,
|
||||||
ConsentMode: b.ConsentMode,
|
ConsentMode: b.ConsentMode,
|
||||||
ShowBranding: b.ShowBranding,
|
ShowBranding: b.ShowBranding,
|
||||||
|
|||||||
Reference in New Issue
Block a user