Skip to content

Commit 77308ca

Browse files
authored
Merge pull request #13525 from quarto-dev/fix/algolia/cookie-support
2 parents 1405db6 + d49b5c1 commit 77308ca

File tree

9 files changed

+118
-6
lines changed

9 files changed

+118
-6
lines changed

news/changelog-1.9.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ All changes included in 1.9:
2828
- ([#13452](https://github.com/quarto-dev/quarto-cli/issues/13452)): Wraps subfigure captions generated by `quarto_super()` in `block` function to avoid emitting `par` elements. (author: @christopherkenny)
2929
- ([#13474](https://github.com/quarto-dev/quarto-cli/issues/13474)): Heading font for title should default to `mainfont`.
3030

31+
## Projects
32+
33+
### `website`
34+
35+
- Algolia Insights now uses privacy-friendly defaults: `useCookie: false` with random session tokens when cookie consent is not configured. When `cookie-consent: true` is enabled, Algolia scripts are deferred and only use cookies after user grants "tracking" consent, ensuring GDPR compliance.
36+
3137
## `publish`
3238

3339
### Confluence

src/project/types/website/website-search.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ const kTitle = "title";
117117
const kText = "text";
118118
const kAnalyticsEvents = "analytics-events";
119119
const kShowLogo = "show-logo";
120+
const kCookieConsentEnabled = "cookie-consent-enabled";
120121

121122
interface SearchOptionsAlgolia {
122123
[kSearchOnlyApiKey]?: string;
@@ -131,6 +132,7 @@ interface SearchOptionsAlgolia {
131132
[kSearchParams]?: Record<string, unknown>;
132133
[kAnalyticsEvents]?: boolean;
133134
[kShowLogo]?: boolean;
135+
[kCookieConsentEnabled]?: boolean;
134136
}
135137

136138
export type SearchInputLocation = "navbar" | "sidebar";
@@ -615,20 +617,28 @@ export async function websiteSearchIncludeInHeader(
615617
}
616618
});
617619

618-
const searchOptionsJson = JSON.stringify(options, null, 2);
619-
const searchOptionsScript =
620-
`<script id="quarto-search-options" type="application/json">${searchOptionsJson}</script>`;
621-
const includes = [searchOptionsScript];
620+
const includes: string[] = [];
622621

622+
// Process all Algolia configuration and scripts
623623
if (options[kAlgolia]) {
624624
includes.push(kAlogioSearchApiScript);
625-
if (options[kAlgolia]?.[kAnalyticsEvents]) {
625+
if (options[kAlgolia][kAnalyticsEvents]) {
626626
const cookieConsent = cookieConsentEnabled(project);
627+
// Set cookie consent flag for JavaScript configuration
628+
options[kAlgolia][kCookieConsentEnabled] = cookieConsent;
629+
// Add Algolia Insights scripts with proper consent handling
627630
includes.push(algoliaSearchInsightsScript(cookieConsent));
628631
includes.push(autocompleteInsightsPluginScript(cookieConsent));
629632
}
630633
}
631634

635+
// Serialize search options to JSON after all modifications
636+
const searchOptionsJson = JSON.stringify(options, null, 2);
637+
const searchOptionsScript =
638+
`<script id="quarto-search-options" type="application/json">${searchOptionsJson}</script>`;
639+
// Prepend search options script to beginning of includes
640+
includes.unshift(searchOptionsScript);
641+
632642
Deno.writeTextFileSync(websiteSearchScript, includes.join("\n"));
633643
return websiteSearchScript;
634644
}

src/resources/projects/website/search/quarto-search.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,19 @@ function configurePlugins(quartoSearchOptions) {
466466
window.aa &&
467467
window["@algolia/autocomplete-plugin-algolia-insights"]
468468
) {
469+
// Check if cookie consent is enabled from search options
470+
const cookieConsentEnabled = algoliaOptions["cookie-consent-enabled"] || false;
471+
472+
// Generate random session token only when cookies are disabled
473+
const userToken = cookieConsentEnabled ? undefined : Array.from(Array(20), () =>
474+
Math.floor(Math.random() * 36).toString(36)
475+
).join("");
476+
469477
window.aa("init", {
470478
appId,
471479
apiKey,
472-
useCookie: true,
480+
useCookie: cookieConsentEnabled,
481+
userToken: userToken,
473482
});
474483

475484
const { createAlgoliaInsightsPlugin } =
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.quarto/
2+
**/*.quarto_ipynb
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
project:
2+
type: website
3+
4+
website:
5+
title: "Algolia Without Cookie Consent Test"
6+
search:
7+
algolia:
8+
application-id: "TEST_APP_ID"
9+
search-only-api-key: "TEST_API_KEY"
10+
index-name: "test-index"
11+
analytics-events: true
12+
# This is the default and should act as such
13+
# cookie-consent: false
14+
15+
format:
16+
html:
17+
theme: cosmo
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: "Algolia Search Without Cookie Consent"
3+
_quarto:
4+
tests:
5+
html:
6+
ensureHtmlElements:
7+
# Ensure search options script exists
8+
- ['script#quarto-search-options']
9+
ensureFileRegexMatches:
10+
-
11+
# Cookie consent should be false
12+
- '"cookie-consent-enabled":\s*false'
13+
# Scripts should load immediately with type="text/javascript"
14+
- '<script[^>]*type="text/javascript">[\s\S]+search-insights'
15+
-
16+
# Verify scripts do NOT have cookie-consent attribute
17+
- '<script[^>]*cookie-consent[^>]+>[\s\S]*search-insights'
18+
---
19+
20+
This test verifies that when `cookie-consent` is NOT configured in the website settings:
21+
22+
1. The Algolia search options JSON contains `"cookie-consent-enabled": false` (or the key is absent)
23+
2. Algolia Insights scripts are loaded immediately with `type="text/javascript"`
24+
3. Scripts are not deferred behind cookie consent (no `cookie-consent="tracking"` attribute)
25+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.quarto/
2+
**/*.quarto_ipynb
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
project:
2+
type: website
3+
4+
website:
5+
title: "Algolia With Cookie Consent Test"
6+
search:
7+
algolia:
8+
application-id: "TEST_APP_ID"
9+
search-only-api-key: "TEST_API_KEY"
10+
index-name: "test-index"
11+
analytics-events: true
12+
cookie-consent: true
13+
14+
format:
15+
html:
16+
theme: cosmo
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: "Algolia Search With Cookie Consent"
3+
_quarto:
4+
tests:
5+
html:
6+
ensureHtmlElements:
7+
-
8+
# Ensure search options script exists
9+
- 'script#quarto-search-options'
10+
# Cookie consent element are loaded
11+
- script[src$='cookie-consent.js']
12+
ensureFileRegexMatches:
13+
-
14+
# Cookie consent should be enabled
15+
- '"cookie-consent-enabled":\s*true'
16+
# Scripts should be deferred with cookie-consent attribute
17+
- 'type="text/plain"[^>]*cookie-consent="tracking"[^>]*>[\s\S]*search-insights'
18+
---
19+
20+
This test verifies that when `cookie-consent` is configured in the website settings:
21+
22+
1. The Algolia search options JSON contains `"cookie-consent-enabled": true`
23+
2. Algolia Insights scripts are deferred with `type="text/plain" cookie-consent="tracking"`
24+
3. Scripts only execute after user grants "tracking" consent
25+
4. Cookie consent UI elements are present on the page

0 commit comments

Comments
 (0)