Matomo integration (#7009)
This commit is contained in:
@@ -2,13 +2,14 @@
|
||||
/* global ga */
|
||||
import escape from "lodash/escape";
|
||||
import * as React from "react";
|
||||
import { IntegrationService } from "@shared/types";
|
||||
import { IntegrationService, PublicEnv } from "@shared/types";
|
||||
import env from "~/env";
|
||||
|
||||
type Props = {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
// TODO: Refactor this component to allow injection from plugins
|
||||
const Analytics: React.FC = ({ children }: Props) => {
|
||||
// Google Analytics 3
|
||||
React.useEffect(() => {
|
||||
@@ -43,12 +44,16 @@ const Analytics: React.FC = ({ children }: Props) => {
|
||||
React.useEffect(() => {
|
||||
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);
|
||||
}
|
||||
|
||||
(env.analytics as PublicEnv["analytics"]).forEach((integration) => {
|
||||
if (integration.service === IntegrationService.GoogleAnalytics) {
|
||||
measurementIds.push(escape(integration.settings?.measurementId));
|
||||
}
|
||||
});
|
||||
|
||||
if (measurementIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -75,6 +80,32 @@ const Analytics: React.FC = ({ children }: Props) => {
|
||||
document.getElementsByTagName("head")[0]?.appendChild(script);
|
||||
}, []);
|
||||
|
||||
// Matomo
|
||||
React.useEffect(() => {
|
||||
(env.analytics as PublicEnv["analytics"]).forEach((integration) => {
|
||||
if (integration.service !== IntegrationService.Matomo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error - Matomo global variable
|
||||
const _paq = (window._paq = window._paq || []);
|
||||
_paq.push(["trackPageView"]);
|
||||
_paq.push(["enableLinkTracking"]);
|
||||
(function () {
|
||||
const u = integration.settings?.instanceUrl;
|
||||
_paq.push(["setTrackerUrl", u + "matomo.php"]);
|
||||
_paq.push(["setSiteId", integration.settings?.measurementId]);
|
||||
const d = document,
|
||||
g = d.createElement("script"),
|
||||
s = d.getElementsByTagName("script")[0];
|
||||
g.type = "text/javascript";
|
||||
g.async = true;
|
||||
g.src = u + "matomo.js";
|
||||
s.parentNode?.insertBefore(g, s);
|
||||
})();
|
||||
});
|
||||
}, []);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import sortBy from "lodash/sortBy";
|
||||
import {
|
||||
EmailIcon,
|
||||
ProfileIcon,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
import React, { ComponentProps } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { integrationSettingsPath } from "@shared/utils/routeHelpers";
|
||||
import GoogleIcon from "~/components/Icons/GoogleIcon";
|
||||
import ZapierIcon from "~/components/Icons/ZapierIcon";
|
||||
import PluginLoader from "~/utils/PluginLoader";
|
||||
import isCloudHosted from "~/utils/isCloudHosted";
|
||||
@@ -32,7 +32,6 @@ const ApiKeys = lazy(() => import("~/scenes/Settings/ApiKeys"));
|
||||
const Details = lazy(() => import("~/scenes/Settings/Details"));
|
||||
const Export = lazy(() => import("~/scenes/Settings/Export"));
|
||||
const Features = lazy(() => import("~/scenes/Settings/Features"));
|
||||
const GoogleAnalytics = lazy(() => import("~/scenes/Settings/GoogleAnalytics"));
|
||||
const Groups = lazy(() => import("~/scenes/Settings/Groups"));
|
||||
const Import = lazy(() => import("~/scenes/Settings/Import"));
|
||||
const Members = lazy(() => import("~/scenes/Settings/Members"));
|
||||
@@ -177,14 +176,6 @@ const useSettingsConfig = () => {
|
||||
group: t("Integrations"),
|
||||
icon: BuildingBlocksIcon,
|
||||
},
|
||||
{
|
||||
name: t("Google Analytics"),
|
||||
path: integrationSettingsPath("google-analytics"),
|
||||
component: GoogleAnalytics,
|
||||
enabled: can.update,
|
||||
group: t("Integrations"),
|
||||
icon: GoogleIcon,
|
||||
},
|
||||
{
|
||||
name: "Zapier",
|
||||
path: integrationSettingsPath("zapier"),
|
||||
@@ -196,30 +187,37 @@ const useSettingsConfig = () => {
|
||||
];
|
||||
|
||||
// Plugins
|
||||
Object.values(PluginLoader.plugins).map((plugin) => {
|
||||
const hasSettings = !!plugin.settings;
|
||||
const enabledInDeployment =
|
||||
!plugin.config?.deployments ||
|
||||
plugin.config.deployments.length === 0 ||
|
||||
(plugin.config.deployments.includes("cloud") && isCloudHosted) ||
|
||||
(plugin.config.deployments.includes("enterprise") && !isCloudHosted);
|
||||
const insertIndex = items.findIndex((i) => i.group === t("Integrations"));
|
||||
items.splice(
|
||||
insertIndex,
|
||||
0,
|
||||
...(sortBy(
|
||||
Object.values(PluginLoader.plugins),
|
||||
(plugin) => plugin.config?.priority ?? 0
|
||||
).map((plugin) => {
|
||||
const hasSettings = !!plugin.settings;
|
||||
const enabledInDeployment =
|
||||
!plugin.config?.deployments ||
|
||||
plugin.config.deployments.length === 0 ||
|
||||
(plugin.config.deployments.includes("community") && !isCloudHosted) ||
|
||||
(plugin.config.deployments.includes("cloud") && isCloudHosted) ||
|
||||
(plugin.config.deployments.includes("enterprise") && !isCloudHosted);
|
||||
|
||||
const item = {
|
||||
name: t(plugin.config.name),
|
||||
path: integrationSettingsPath(plugin.id),
|
||||
// TODO: Remove hardcoding of plugin id here
|
||||
group: plugin.id === "collections" ? t("Workspace") : t("Integrations"),
|
||||
component: plugin.settings,
|
||||
enabled:
|
||||
enabledInDeployment &&
|
||||
hasSettings &&
|
||||
(plugin.config.roles?.includes(user.role) || can.update),
|
||||
icon: plugin.icon,
|
||||
} as ConfigItem;
|
||||
|
||||
const insertIndex = items.findIndex((i) => i.group === t("Integrations"));
|
||||
items.splice(insertIndex, 0, item);
|
||||
});
|
||||
return {
|
||||
name: t(plugin.config.name),
|
||||
path: integrationSettingsPath(plugin.id),
|
||||
// TODO: Remove hardcoding of plugin id here
|
||||
group:
|
||||
plugin.id === "collections" ? t("Workspace") : t("Integrations"),
|
||||
component: plugin.settings,
|
||||
enabled:
|
||||
enabledInDeployment &&
|
||||
hasSettings &&
|
||||
(plugin.config.roles?.includes(user.role) || can.update),
|
||||
icon: plugin.icon,
|
||||
};
|
||||
}) as ConfigItem[])
|
||||
);
|
||||
|
||||
return items;
|
||||
}, [t, can.createApiKey, can.update, can.createImport, can.createExport]);
|
||||
|
||||
@@ -8,6 +8,7 @@ interface Plugin {
|
||||
description: string;
|
||||
roles?: UserRole[];
|
||||
deployments?: string[];
|
||||
priority?: number;
|
||||
};
|
||||
settings: React.FC;
|
||||
icon: React.FC<{ size?: number; fill?: string }>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "google",
|
||||
"name": "Google",
|
||||
"priority": 10,
|
||||
"priority": 0,
|
||||
"description": "Adds a Google authentication provider."
|
||||
}
|
||||
|
||||
26
plugins/googleanalytics/client/Icon.tsx
Normal file
26
plugins/googleanalytics/client/Icon.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
/** The size of the icon, 24px is default to match standard icons */
|
||||
size?: number;
|
||||
/** The color of the icon, defaults to the current text color */
|
||||
fill?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function GoogleLogo({ size = 24, fill = "currentColor", className }: Props) {
|
||||
return (
|
||||
<svg
|
||||
fill={fill}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path d="M19.2312 10.5455H11.8276V13.6364H16.0892C15.6919 15.6 14.0306 16.7273 11.8276 16.7273C9.22733 16.7273 7.13267 14.6182 7.13267 12C7.13267 9.38182 9.22733 7.27273 11.8276 7.27273C12.9472 7.27273 13.9584 7.67273 14.7529 8.32727L17.0643 6C15.6558 4.76364 13.85 4 11.8276 4C7.42159 4 3.88232 7.56364 3.88232 12C3.88232 16.4364 7.42159 20 11.8276 20C15.8002 20 19.4117 17.0909 19.4117 12C19.4117 11.5273 19.3395 11.0182 19.2312 10.5455Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default GoogleLogo;
|
||||
@@ -6,6 +6,7 @@ import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import Integration from "~/models/Integration";
|
||||
import SettingRow from "~/scenes/Settings/components/SettingRow";
|
||||
import Button from "~/components/Button";
|
||||
import Heading from "~/components/Heading";
|
||||
import GoogleIcon from "~/components/Icons/GoogleIcon";
|
||||
@@ -13,7 +14,6 @@ import Input from "~/components/Input";
|
||||
import Scene from "~/components/Scene";
|
||||
import Text from "~/components/Text";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import SettingRow from "./components/SettingRow";
|
||||
|
||||
type FormData = {
|
||||
measurementId: string;
|
||||
6
plugins/googleanalytics/plugin.json
Normal file
6
plugins/googleanalytics/plugin.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"id": "googleanalytics",
|
||||
"name": "Google Analytics",
|
||||
"priority": 30,
|
||||
"description": "Adds support for reporting analytics to a Google."
|
||||
}
|
||||
22
plugins/matomo/client/Icon.tsx
Normal file
22
plugins/matomo/client/Icon.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
/** The size of the icon, 24px is default to match standard icons */
|
||||
size?: number;
|
||||
/** The color of the icon, defaults to the current text color */
|
||||
fill?: string;
|
||||
};
|
||||
|
||||
export default function Icon({ size = 24, fill = "currentColor" }: Props) {
|
||||
return (
|
||||
<svg
|
||||
fill={fill}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
>
|
||||
<path d="M15.7624 15.309L18.2171 11.588C18.5688 11.0797 18.7702 10.4684 18.7702 9.81395C18.7702 9.72425 18.7668 9.63787 18.76 9.5515L21.4981 13.6113C21.5084 13.6246 21.5118 13.6346 21.522 13.6478L21.5732 13.7243L21.5698 13.7276C21.8395 14.1561 21.9966 14.6578 22 15.1927C22 16.7409 20.7095 17.9967 19.1185 17.9967C18.142 17.9967 17.2851 17.5249 16.7627 16.804L16.7593 16.8073L16.7422 16.7807C16.7286 16.7608 16.7149 16.7375 16.6979 16.7176M15.5336 14.9635L17.1041 12.5814C15.1767 13.6317 12.5053 12.4636 12.2253 10.1993C12.2253 10.1927 12.2253 10.186 12.2219 10.1794L10.8938 8.35216H10.8904C10.3817 7.5515 9.47695 7.01661 8.43564 7.01661C8.43223 7.01661 8.43223 7.01661 8.42882 7.01661C8.4254 7.01661 8.4254 7.01661 8.42199 7.01661C7.38409 7.01661 6.47593 7.5515 5.97064 8.35216H5.96722L3.29737 12.4086C3.76852 12.1528 4.30795 12.01 4.88153 12.01C6.55446 12.01 7.93718 13.2359 8.13179 14.8106L9.63059 16.8571H9.63401C10.1564 17.5482 10.9997 18 11.9522 18H11.959H11.9659C12.9184 18 13.7583 17.5515 14.2841 16.8571H14.2875L14.3114 16.8206C14.3694 16.7409 14.424 16.6578 14.4787 16.5681L15.5336 14.9668V14.9635ZM12.6009 9.80731C12.6009 11.3588 13.8914 12.6146 15.4858 12.6146C17.0802 12.6146 18.3708 11.3588 18.3708 9.80731C18.3708 8.25581 17.0802 7 15.4858 7C13.8914 7 12.6009 8.25581 12.6009 9.80731ZM2 15.196C2 16.7442 3.29054 18 4.88153 18C6.47252 18 7.76306 16.7442 7.76306 15.196C7.76306 13.6478 6.47252 12.392 4.88153 12.392C3.29054 12.392 2 13.6478 2 15.196Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
128
plugins/matomo/client/Settings.tsx
Normal file
128
plugins/matomo/client/Settings.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import find from "lodash/find";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import Integration from "~/models/Integration";
|
||||
import SettingRow from "~/scenes/Settings/components/SettingRow";
|
||||
import Button from "~/components/Button";
|
||||
import Heading from "~/components/Heading";
|
||||
import Input from "~/components/Input";
|
||||
import Scene from "~/components/Scene";
|
||||
import Text from "~/components/Text";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import Icon from "./Icon";
|
||||
|
||||
type FormData = {
|
||||
instanceUrl: string;
|
||||
measurementId: string;
|
||||
};
|
||||
|
||||
function Matomo() {
|
||||
const { integrations } = useStores();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const integration = find(integrations.orderedData, {
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.Matomo,
|
||||
}) as Integration<IntegrationType.Analytics> | undefined;
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit: formHandleSubmit,
|
||||
formState,
|
||||
} = useForm<FormData>({
|
||||
mode: "all",
|
||||
defaultValues: {
|
||||
instanceUrl: integration?.settings.instanceUrl,
|
||||
measurementId: integration?.settings.measurementId,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
void integrations.fetchPage({
|
||||
type: IntegrationType.Analytics,
|
||||
});
|
||||
}, [integrations]);
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
measurementId: integration?.settings.measurementId,
|
||||
instanceUrl: integration?.settings.instanceUrl,
|
||||
});
|
||||
}, [integration, reset]);
|
||||
|
||||
const handleSubmit = React.useCallback(
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
if (data.instanceUrl && data.measurementId) {
|
||||
await integrations.save({
|
||||
id: integration?.id,
|
||||
type: IntegrationType.Analytics,
|
||||
service: IntegrationService.Matomo,
|
||||
settings: {
|
||||
measurementId: data.measurementId,
|
||||
// Ensure the URL ends with a trailing slash
|
||||
instanceUrl: data.instanceUrl.replace(/\/?$/, "/"),
|
||||
} as Integration<IntegrationType.Analytics>["settings"],
|
||||
});
|
||||
} else {
|
||||
await integration?.delete();
|
||||
}
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<Scene title="Matomo" icon={<Icon />}>
|
||||
<Heading>Matomo</Heading>
|
||||
|
||||
<Text as="p" type="secondary">
|
||||
<Trans>
|
||||
Configure a Matomo installation to send views and analytics from the
|
||||
workspace to your own Matomo instance.
|
||||
</Trans>
|
||||
</Text>
|
||||
<form onSubmit={formHandleSubmit(handleSubmit)}>
|
||||
<SettingRow
|
||||
label={t("Instance URL")}
|
||||
name="instanceUrl"
|
||||
description={t(
|
||||
"The URL of your Matomo instance. If you are using Matomo Cloud it will end in matomo.cloud/"
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input
|
||||
required
|
||||
placeholder="https://instance.matomo.cloud/"
|
||||
{...register("instanceUrl")}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
label={t("Site ID")}
|
||||
name="measurementId"
|
||||
description={t(
|
||||
"An ID that uniquely identifies the website in your Matomo instance."
|
||||
)}
|
||||
border={false}
|
||||
>
|
||||
<Input required placeholder="1" {...register("measurementId")} />
|
||||
</SettingRow>
|
||||
|
||||
<Button type="submit" disabled={formState.isSubmitting}>
|
||||
{formState.isSubmitting ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(Matomo);
|
||||
8
plugins/matomo/plugin.json
Normal file
8
plugins/matomo/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "matomo",
|
||||
"name": "Matomo",
|
||||
"roles": ["admin"],
|
||||
"priority": 40,
|
||||
"description": "Adds support for reporting analytics to a Matomo server.",
|
||||
"deployments": ["community", "enterprise"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "oidc",
|
||||
"name": "OIDC",
|
||||
"priority": 30,
|
||||
"priority": 10,
|
||||
"description": "Adds an OpenID compatible authentication provider."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "slack",
|
||||
"name": "Slack",
|
||||
"priority": 40,
|
||||
"priority": 20,
|
||||
"roles": ["admin", "member"],
|
||||
"description": "Adds a Slack authentication provider, support for the /outline slash command, and link unfurling."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "webhooks",
|
||||
"name": "Webhooks",
|
||||
"priority": 200,
|
||||
"priority": 0,
|
||||
"description": "Adds HTTP webhooks for various events."
|
||||
}
|
||||
|
||||
@@ -7,16 +7,16 @@ import { Integration } from "@server/models";
|
||||
export default function present(
|
||||
env: Environment,
|
||||
options: {
|
||||
analytics?: Integration<IntegrationType.Analytics> | null;
|
||||
analytics?: Integration<IntegrationType.Analytics>[];
|
||||
rootShareId?: string | null;
|
||||
} = {}
|
||||
): PublicEnv {
|
||||
return {
|
||||
ROOT_SHARE_ID: options.rootShareId || undefined,
|
||||
analytics: {
|
||||
service: options.analytics?.service,
|
||||
settings: options.analytics?.settings,
|
||||
},
|
||||
analytics: (options.analytics ?? []).map((integration) => ({
|
||||
service: integration?.service,
|
||||
settings: integration?.settings,
|
||||
})),
|
||||
...env.public,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,7 +51,12 @@ export const IntegrationsCreateSchema = BaseSchema.extend({
|
||||
channelId: z.string(),
|
||||
})
|
||||
)
|
||||
.or(z.object({ measurementId: z.string() }))
|
||||
.or(
|
||||
z.object({
|
||||
measurementId: z.string(),
|
||||
instanceUrl: z.string().url().optional(),
|
||||
})
|
||||
)
|
||||
.or(z.object({ serviceTeamId: z.string() }))
|
||||
.optional(),
|
||||
}),
|
||||
@@ -74,7 +79,12 @@ export const IntegrationsUpdateSchema = BaseSchema.extend({
|
||||
channelId: z.string(),
|
||||
})
|
||||
)
|
||||
.or(z.object({ measurementId: z.string() }))
|
||||
.or(
|
||||
z.object({
|
||||
measurementId: z.string(),
|
||||
instanceUrl: z.string().url().optional(),
|
||||
})
|
||||
)
|
||||
.or(z.object({ serviceTeamId: z.string() }))
|
||||
.optional(),
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const renderApp = async (
|
||||
canonical?: string;
|
||||
shortcutIcon?: string;
|
||||
rootShareId?: string;
|
||||
analytics?: Integration | null;
|
||||
analytics?: Integration<IntegrationType.Analytics>[];
|
||||
} = {}
|
||||
) => {
|
||||
const {
|
||||
@@ -66,6 +66,19 @@ export const renderApp = async (
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!env.isCloudHosted) {
|
||||
options.analytics?.forEach((integration) => {
|
||||
if (integration.settings?.instanceUrl) {
|
||||
const parsed = new URL(integration.settings?.instanceUrl);
|
||||
const csp = ctx.response.get("Content-Security-Policy");
|
||||
ctx.set(
|
||||
"Content-Security-Policy",
|
||||
csp.replace("script-src", `script-src ${parsed.hostname}`)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { shareId } = ctx.params;
|
||||
const page = await readIndexFile();
|
||||
const environment = `
|
||||
@@ -112,7 +125,8 @@ export const renderShare = async (ctx: Context, next: Next) => {
|
||||
// Find the share record if publicly published so that the document title
|
||||
// can be be returned in the server-rendered HTML. This allows it to appear in
|
||||
// unfurls with more reliablity
|
||||
let share, document, team, analytics;
|
||||
let share, document, team;
|
||||
let analytics: Integration<IntegrationType.Analytics>[] = [];
|
||||
|
||||
try {
|
||||
team = await getTeamFromContext(ctx);
|
||||
@@ -131,7 +145,7 @@ export const renderShare = async (ctx: Context, next: Next) => {
|
||||
}
|
||||
document = result.document;
|
||||
|
||||
analytics = await Integration.findOne({
|
||||
analytics = await Integration.findAll({
|
||||
where: {
|
||||
teamId: document.teamId,
|
||||
type: IntegrationType.Analytics,
|
||||
|
||||
@@ -147,13 +147,13 @@ router.get("*", shareDomains(), async (ctx, next) => {
|
||||
}
|
||||
|
||||
const analytics = team
|
||||
? await Integration.findOne({
|
||||
? await Integration.findAll({
|
||||
where: {
|
||||
teamId: team.id,
|
||||
type: IntegrationType.Analytics,
|
||||
},
|
||||
})
|
||||
: undefined;
|
||||
: [];
|
||||
|
||||
return renderApp(ctx, next, {
|
||||
analytics,
|
||||
|
||||
@@ -443,7 +443,6 @@
|
||||
"Import": "Import",
|
||||
"Self Hosted": "Self Hosted",
|
||||
"Integrations": "Integrations",
|
||||
"Google Analytics": "Google Analytics",
|
||||
"Choose a template": "Choose a template",
|
||||
"Revoke token": "Revoke token",
|
||||
"Revoke": "Revoke",
|
||||
@@ -842,9 +841,6 @@
|
||||
"When enabled documents have a separate editing mode by default instead of being always editable. This setting can be overridden by user preferences.": "When enabled documents have a separate editing mode by default instead of being always editable. This setting can be overridden by user preferences.",
|
||||
"Commenting": "Commenting",
|
||||
"When enabled team members can add comments to documents.": "When enabled team members can add comments to documents.",
|
||||
"Add a Google Analytics 4 measurement ID to send document views and analytics from the workspace to your own Google Analytics account.": "Add a Google Analytics 4 measurement ID to send document views and analytics from the workspace to your own Google Analytics account.",
|
||||
"Measurement ID": "Measurement ID",
|
||||
"Create a \"Web\" stream in your Google Analytics admin dashboard and copy the measurement ID from the generated code snippet to install.": "Create a \"Web\" stream in your Google Analytics admin dashboard and copy the measurement ID from the generated code snippet to install.",
|
||||
"New group": "New group",
|
||||
"Groups can be used to organize and manage the people on your team.": "Groups can be used to organize and manage the people on your team.",
|
||||
"No groups have been created yet": "No groups have been created yet",
|
||||
@@ -969,6 +965,15 @@
|
||||
"Enabled by {{integrationCreatedBy}}": "Enabled by {{integrationCreatedBy}}",
|
||||
"Disconnecting will prevent previewing GitHub links from this organization in documents. Are you sure?": "Disconnecting will prevent previewing GitHub links from this organization in documents. Are you sure?",
|
||||
"The GitHub integration is currently disabled. Please set the associated environment variables and restart the server to enable the integration.": "The GitHub integration is currently disabled. Please set the associated environment variables and restart the server to enable the integration.",
|
||||
"Google Analytics": "Google Analytics",
|
||||
"Add a Google Analytics 4 measurement ID to send document views and analytics from the workspace to your own Google Analytics account.": "Add a Google Analytics 4 measurement ID to send document views and analytics from the workspace to your own Google Analytics account.",
|
||||
"Measurement ID": "Measurement ID",
|
||||
"Create a \"Web\" stream in your Google Analytics admin dashboard and copy the measurement ID from the generated code snippet to install.": "Create a \"Web\" stream in your Google Analytics admin dashboard and copy the measurement ID from the generated code snippet to install.",
|
||||
"Configure a Matomo installation to send views and analytics from the workspace to your own Matomo instance.": "Configure a Matomo installation to send views and analytics from the workspace to your own Matomo instance.",
|
||||
"Instance URL": "Instance URL",
|
||||
"The URL of your Matomo instance. If you are using Matomo Cloud it will end in matomo.cloud/": "The URL of your Matomo instance. If you are using Matomo Cloud it will end in matomo.cloud/",
|
||||
"Site ID": "Site ID",
|
||||
"An ID that uniquely identifies the website in your Matomo instance.": "An ID that uniquely identifies the website in your Matomo instance.",
|
||||
"Add to Slack": "Add to Slack",
|
||||
"document published": "document published",
|
||||
"document updated": "document updated",
|
||||
|
||||
@@ -52,9 +52,9 @@ export enum MentionType {
|
||||
export type PublicEnv = {
|
||||
ROOT_SHARE_ID?: string;
|
||||
analytics: {
|
||||
service?: IntegrationService;
|
||||
settings?: IntegrationSettings<IntegrationType.Analytics>;
|
||||
};
|
||||
service: IntegrationService;
|
||||
settings: IntegrationSettings<IntegrationType.Analytics>;
|
||||
}[];
|
||||
};
|
||||
|
||||
export enum AttachmentPreset {
|
||||
@@ -82,6 +82,7 @@ export enum IntegrationService {
|
||||
Grist = "grist",
|
||||
Slack = "slack",
|
||||
GoogleAnalytics = "google-analytics",
|
||||
Matomo = "matomo",
|
||||
GitHub = "github",
|
||||
}
|
||||
|
||||
@@ -90,12 +91,14 @@ export type UserCreatableIntegrationService = Extract<
|
||||
| IntegrationService.Diagrams
|
||||
| IntegrationService.Grist
|
||||
| IntegrationService.GoogleAnalytics
|
||||
| IntegrationService.Matomo
|
||||
>;
|
||||
|
||||
export const UserCreatableIntegrationService = {
|
||||
Diagrams: IntegrationService.Diagrams,
|
||||
Grist: IntegrationService.Grist,
|
||||
GoogleAnalytics: IntegrationService.GoogleAnalytics,
|
||||
Matomo: IntegrationService.Matomo,
|
||||
} as const;
|
||||
|
||||
export enum CollectionPermission {
|
||||
@@ -121,7 +124,7 @@ export type IntegrationSettings<T> = T extends IntegrationType.Embed
|
||||
};
|
||||
}
|
||||
: T extends IntegrationType.Analytics
|
||||
? { measurementId: string }
|
||||
? { measurementId: string; instanceUrl?: string }
|
||||
: T extends IntegrationType.Post
|
||||
? { url: string; channel: string; channelId: string }
|
||||
: T extends IntegrationType.Command
|
||||
|
||||
Reference in New Issue
Block a user