diff --git a/app.json b/app.json index e7d5919b76..a062974554 100644 --- a/app.json +++ b/app.json @@ -3,13 +3,7 @@ "description": "Open source wiki and knowledge base for growing teams", "website": "https://www.getoutline.com/", "repository": "https://github.com/outline/outline", - "keywords": [ - "wiki", - "team", - "node", - "markdown", - "slack" - ], + "keywords": ["wiki", "team", "node", "markdown", "slack"], "success_url": "/", "formation": { "web": { @@ -188,7 +182,7 @@ "required": false }, "GOOGLE_ANALYTICS_ID": { - "description": "UA-xxxx (optional)", + "description": "G-xxxx (optional)", "required": false }, "SENTRY_DSN": { diff --git a/app/components/Analytics.tsx b/app/components/Analytics.tsx index 79af34d880..8f090f9939 100644 --- a/app/components/Analytics.tsx +++ b/app/components/Analytics.tsx @@ -8,7 +8,7 @@ import env from "~/env"; const Analytics: React.FC = ({ children }) => { // Google Analytics 3 React.useEffect(() => { - if (!env.GOOGLE_ANALYTICS_ID) { + if (!env.GOOGLE_ANALYTICS_ID?.startsWith("UA-")) { return; } @@ -37,25 +37,38 @@ const Analytics: React.FC = ({ children }) => { // Google Analytics 4 React.useEffect(() => { - if (env.analytics.service !== IntegrationService.GoogleAnalytics) { + const measurementIds = []; + + if (env.analytics.service === IntegrationService.GoogleAnalytics) { + measurementIds.push(escape(env.analytics.settings?.measurementId)); + } + if (env.GOOGLE_ANALYTICS_ID?.startsWith("G-")) { + measurementIds.push(env.GOOGLE_ANALYTICS_ID); + } + if (measurementIds.length === 0) { return; } - const measurementId = escape(env.analytics.settings?.measurementId); + const params = { + allow_google_signals: false, + restricted_data_processing: true, + }; window.dataLayer = window.dataLayer || []; window.gtag = function () { window.dataLayer.push(arguments); }; window.gtag("js", new Date()); - window.gtag("config", measurementId, { - allow_google_signals: false, - restricted_data_processing: true, - }); + + for (const measurementId of measurementIds) { + window.gtag("config", measurementId, params); + } const script = document.createElement("script"); script.type = "text/javascript"; - script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`; + script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementIds.join( + "," + )}`; script.async = true; document.getElementsByTagName("head")[0]?.appendChild(script); }, []); diff --git a/server/env.ts b/server/env.ts index d218dbc1f7..75b36e28f1 100644 --- a/server/env.ts +++ b/server/env.ts @@ -16,7 +16,6 @@ import { IsIn, IsEmail, IsBoolean, - Contains, MaxLength, } from "class-validator"; import { languages } from "@shared/i18n"; @@ -344,9 +343,8 @@ export class Environment { public RELEASE = this.toOptionalString(process.env.RELEASE); /** - * A Google Analytics tracking ID, supports only v3 properties. + * A Google Analytics tracking ID, supports v3 or v4 properties. */ - @Contains("UA-") @IsOptional() public GOOGLE_ANALYTICS_ID = this.toOptionalString( process.env.GOOGLE_ANALYTICS_ID