Skip to content

Commit d53d1b7

Browse files
committed
Merge branch 'develop' into release/release-8
2 parents f66cfdd + 94307ac commit d53d1b7

File tree

14 files changed

+80
-43
lines changed

14 files changed

+80
-43
lines changed

src/core/apollo/generated/apollo-hooks.ts

+5
Original file line numberDiff line numberDiff line change
@@ -18769,6 +18769,11 @@ export const ConfigurationDocument = gql`
1876918769
configuration {
1877018770
...Configuration
1877118771
}
18772+
settings {
18773+
integration {
18774+
iframeAllowedUrls
18775+
}
18776+
}
1877218777
}
1877318778
}
1877418779
${ConfigurationFragmentDoc}

src/core/apollo/generated/graphql-schema.ts

+4
Original file line numberDiff line numberDiff line change
@@ -23172,6 +23172,10 @@ export type ConfigurationQuery = {
2317223172
apm: { __typename?: 'APM'; rumEnabled: boolean; endpoint: string };
2317323173
geo: { __typename?: 'Geo'; endpoint: string };
2317423174
};
23175+
settings: {
23176+
__typename?: 'PlatformSettings';
23177+
integration: { __typename?: 'PlatformIntegrationSettings'; iframeAllowedUrls: Array<string> };
23178+
};
2317523179
};
2317623180
};
2317723181

src/core/apollo/graphqlLinks/httpLink.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const httpLink = (graphQLEndpoint: string, enableWebSockets: boolean) =>
5050
uploadLink
5151
);
5252
} catch (error) {
53-
logWarn(error as Error, { category: TagCategoryValues.WS });
53+
logWarn(error as Error, { category: TagCategoryValues.WS, label: 'Failed to create ws link' });
5454
}
5555
}
5656
return uploadLink;

src/core/i18n/en/translation.en.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@
877877
"findByUrl": {
878878
"title": "Subspace URL",
879879
"description": "Enter the URL of the Subspace you want to copy as a template.",
880-
"use": "Use this space",
880+
"use": "Use this Subspace",
881881
"spaceNotFoundError": "Space not found",
882882
"collaborationNotFoundError": "Collaboration not found",
883883
"selectAnother": "Update Collaboration from another Space"
@@ -3127,7 +3127,7 @@
31273127
},
31283128
"comingSoon": {
31293129
"title": "🚀 Coming soon!",
3130-
"subTitle": "We're currently building the functionality to use external AI services for your Virtual Contributor. You can help us by sharing which AI service you would like to use.",
3130+
"subTitle": "Looking for a different AI service? Let us know!",
31313131
"input": {
31323132
"label": "AI Service",
31333133
"placeholder": "E.g. the AI that xx created to xx"
@@ -3156,7 +3156,7 @@
31563156
"title": "Choose a (Sub)Space",
31573157
"description": "Which Space or Subspace do you want to bring to life with the Virtual Contributor? Below you'll be able to choose from all Spaces and Subspaces that you are host of.",
31583158
"label": "(Sub)Space",
3159-
"noSpaces": "You need a Space to proceed with this option. To create a Virtual Contributor, please go back and follow the Written Knowledge flow."
3159+
"noSpaces": "You need a Space to proceed with this option. To create a Virtual Contributor without a Space, please go back and follow the Written Knowledge flow."
31603160
},
31613161
"chooseCommunity": {
31623162
"title": "Hello, {{vcName}}!",

src/core/ui/forms/MarkdownInputControls/InsertEmbedCodeButton/InsertEmbedCodeButton.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import DialogHeader from '@/core/ui/dialog/DialogHeader';
1414
import DialogWithGrid from '@/core/ui/dialog/DialogWithGrid';
1515
import { MARKDOWN_TEXT_LENGTH } from '@/core/ui/forms/field-length.constants';
1616
import { gutters } from '@/core/ui/grid/utils';
17-
import { ALLOWED_EMBED_URLS } from '@/core/ui/markdown/embed/allowedEmbedUrls';
17+
import { useConfig } from '@/domain/platform/config/useConfig';
1818

1919
interface InsertEmbedCodeButtonProps extends IconButtonProps {
2020
editor: Editor | null;
@@ -34,6 +34,8 @@ export const InsertEmbedCodeButton = ({
3434

3535
const notify = useNotification();
3636

37+
const { integration: { iframeAllowedUrls = [] } = {} } = useConfig();
38+
3739
const buttonRef = useRef<HTMLButtonElement>(null);
3840

3941
const textareaRef = useRef<HTMLTextAreaElement>(null);
@@ -98,7 +100,7 @@ export const InsertEmbedCodeButton = ({
98100
}
99101

100102
const srcOrigin = new URL(embedCodeSrc).origin;
101-
const isValidSource = ALLOWED_EMBED_URLS.some(vS => vS === srcOrigin);
103+
const isValidSource = iframeAllowedUrls.some(vS => vS === srcOrigin);
102104

103105
if (!isValidSource) {
104106
notify(t('components.wysiwyg-editor.embed.invalidOrUnsupportedEmbed'), 'error');

src/core/ui/markdown/WrapperMarkdown.tsx

+29-20
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import PlainText from './PlainText';
66
import { MarkdownOptions, MarkdownOptionsProvider } from './MarkdownOptionsContext';
77
import { Box } from '@mui/material';
88
import { remarkVerifyIframe } from './embed/remarkVerifyIframe';
9+
import { useConfig } from '@/domain/platform/config/useConfig';
910

1011
const allowedNodeTypes = ['iframe'] as const;
1112

@@ -21,25 +22,33 @@ export const WrapperMarkdown = ({
2122
caption = false,
2223
sx,
2324
...props
24-
}: MarkdownProps) => (
25-
<MarkdownOptionsProvider
26-
card={card}
27-
plain={plain}
28-
multiline={multiline}
29-
disableParagraphPadding={disableParagraphPadding}
30-
caption={caption}
31-
>
32-
<Box sx={{ li: { marginY: caption ? 0 : 1 }, display: plain ? 'inline' : undefined, ...sx }}>
33-
<ReactMarkdown
34-
components={components}
35-
remarkPlugins={[gfm, [PlainText, { enabled: plain }], remarkVerifyIframe]}
36-
rehypePlugins={
37-
plain ? undefined : ([rehypeRaw, { passThrough: allowedNodeTypes }] as MarkdownProps['rehypePlugins'])
38-
}
39-
{...props}
40-
/>
41-
</Box>
42-
</MarkdownOptionsProvider>
43-
);
25+
}: MarkdownProps) => {
26+
const { integration: { iframeAllowedUrls = [] } = {} } = useConfig();
27+
28+
return (
29+
<MarkdownOptionsProvider
30+
card={card}
31+
plain={plain}
32+
multiline={multiline}
33+
disableParagraphPadding={disableParagraphPadding}
34+
caption={caption}
35+
>
36+
<Box sx={{ li: { marginY: caption ? 0 : 1 }, display: plain ? 'inline' : undefined, ...sx }}>
37+
<ReactMarkdown
38+
components={components}
39+
remarkPlugins={[
40+
gfm,
41+
[PlainText, { enabled: plain }],
42+
[remarkVerifyIframe, { allowedIFrameOrigins: iframeAllowedUrls }],
43+
]}
44+
rehypePlugins={
45+
plain ? undefined : ([rehypeRaw, { passThrough: allowedNodeTypes }] as MarkdownProps['rehypePlugins'])
46+
}
47+
{...props}
48+
/>
49+
</Box>
50+
</MarkdownOptionsProvider>
51+
);
52+
};
4453

4554
export default WrapperMarkdown;

src/core/ui/markdown/embed/allowedEmbedUrls.ts

-7
This file was deleted.

src/core/ui/markdown/embed/remarkVerifyIframe.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { Pluggable } from 'unified';
1+
import { Plugin } from 'unified';
22
import { visit } from 'unist-util-visit';
33

4-
import { ALLOWED_EMBED_URLS } from './allowedEmbedUrls';
5-
6-
const isAllowedUrl = (url: string) => {
4+
const isAllowedUrl = (url: string, allowedIFrameOrigins: string[]) => {
75
try {
86
const parsedUrl = new URL(url);
97

@@ -13,18 +11,20 @@ const isAllowedUrl = (url: string) => {
1311

1412
const srcOrigin = parsedUrl.origin;
1513

16-
return ALLOWED_EMBED_URLS.some(vS => vS === srcOrigin);
14+
return allowedIFrameOrigins.some(vS => vS === srcOrigin);
1715
} catch (e) {
1816
console.error('Invalid iframe URL:', url, e);
1917

2018
return false;
2119
}
2220
};
2321

24-
export const remarkVerifyIframe: Pluggable = () => {
22+
export const remarkVerifyIframe: Plugin<[{ allowedIFrameOrigins?: string[] }]> = ({
23+
allowedIFrameOrigins = [],
24+
} = {}) => {
2525
return tree => {
2626
visit(tree, 'html', (node: { value: string }) => {
27-
if (node && typeof node.value === 'string') {
27+
if (node && node.value) {
2828
const nodeValue: string = node.value;
2929
if (nodeValue.toLowerCase().includes('<iframe')) {
3030
try {
@@ -38,7 +38,7 @@ export const remarkVerifyIframe: Pluggable = () => {
3838
} else {
3939
for (let iframe of iframes) {
4040
const src = iframe.getAttribute('src');
41-
if (!src || !isAllowedUrl(src)) {
41+
if (!src || !isAllowedUrl(src, allowedIFrameOrigins)) {
4242
node.value = '';
4343
return;
4444
}

src/domain/collaboration/whiteboard/WhiteboardDialog/WhiteboardDialog.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface WhiteboardDetails {
4747
preview?: {
4848
id: string;
4949
} & PreviewImageDimensions;
50+
url?: string;
5051
};
5152
createdBy?: {
5253
id: string;

src/domain/common/whiteboard/excalidraw/CollaborativeExcalidrawWrapper.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import useWhiteboardDefaults from './useWhiteboardDefaults';
2828
import Loading from '@/core/ui/loading/Loading';
2929
import { Identifiable } from '@/core/utils/Identifiable';
3030
import { lazyWithGlobalErrorHandler } from '@/core/lazyLoading/lazyWithGlobalErrorHandler';
31+
import { TagCategoryValues, error as logError } from '@/core/logging/sentry/log';
3132

3233
const FILE_IMPORT_ENABLED = true;
3334
const SAVE_FILE_TO_DISK = true;
@@ -73,7 +74,7 @@ const useActorWhiteboardStyles = makeStyles(theme => ({
7374
}));
7475

7576
export interface WhiteboardWhiteboardEntities {
76-
whiteboard: Identifiable | undefined;
77+
whiteboard: (Identifiable & { profile?: { url?: string } }) | undefined;
7778
filesManager: WhiteboardFilesManager;
7879
lastSavedDate: Date | undefined;
7980
}
@@ -165,6 +166,12 @@ const CollaborativeExcalidrawWrapper = ({
165166
if (isOnline) {
166167
setupReconnectTimeout();
167168
}
169+
// event if it's duplicated by the httpLink and Portal handlers, let's log this closeConnection one
170+
// with additional info here #7492
171+
logError('WB Connection Closed', {
172+
category: TagCategoryValues.WHITEBOARD,
173+
label: `WB ID: ${whiteboard?.id}; URL: ${whiteboard?.profile?.url}; Online: ${isOnline}`,
174+
});
168175
},
169176
onInitialize: collabApi => {
170177
combinedCollabApiRef.current = collabApi;

src/domain/platform/config/ConfigProvider.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ const ConfigProvider: FC<ConfigProviderProps> = ({ children, url }) => {
3030
const [requestConfig, loading, error] = useLoadingStateWithHandlers(
3131
async (url: string) => {
3232
const result = await queryRequest<ConfigurationQuery>(url, ConfigurationDocument);
33-
setConfig(result.data.data.platform.configuration);
33+
const platformConfiguration = result.data.data.platform.configuration;
34+
const settings = result.data.data.platform.settings;
35+
const combinedConfiguration: Configuration = {
36+
...platformConfiguration,
37+
...settings,
38+
};
39+
40+
setConfig(combinedConfiguration);
3441
},
3542
{
3643
onError: err => logWarn(err, { category: TagCategoryValues.CONFIG }),

src/domain/platform/config/configuration.graphql

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ query configuration {
33
configuration {
44
...Configuration
55
}
6+
settings {
7+
integration {
8+
iframeAllowedUrls
9+
}
10+
}
611
}
712
}
813

src/domain/platform/config/configuration.ts

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ export interface Configuration {
4141
geo: {
4242
endpoint: string;
4343
};
44+
integration: {
45+
iframeAllowedUrls: string[];
46+
};
4447
}
4548

4649
interface AuthenticationProvider {

src/domain/platform/config/useConfig.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const useConfig = () => {
1717
authentication: context.config?.authentication,
1818
locations: context.config?.locations,
1919
features: context.config?.featureFlags,
20+
integration: context.config?.integration,
2021
sentry: context.config?.sentry,
2122
apm: context.config?.apm,
2223
geo: context.config?.geo,

0 commit comments

Comments
 (0)