Skip to content

Commit d2a8e42

Browse files
committed
🦺(frontend) check content type pdf on PdfBlock
Pdfblock was quite permissive on the content type it was accepting. Now it checks that the content type is exactly 'application/pdf' before rendering the PDF viewer.
1 parent c2ec434 commit d2a8e42

File tree

1 file changed

+66
-20
lines changed
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks

1 file changed

+66
-20
lines changed

src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ import {
1313
createReactBlockSpec,
1414
} from '@blocknote/react';
1515
import { TFunction } from 'i18next';
16-
import { useEffect } from 'react';
16+
import { useEffect, useState } from 'react';
1717
import { useTranslation } from 'react-i18next';
18-
import { createGlobalStyle } from 'styled-components';
18+
import { createGlobalStyle, css } from 'styled-components';
1919

20-
import { Box, Icon } from '@/components';
20+
import { Box, Icon, Loading } from '@/components';
2121

2222
import { DocsBlockNoteEditor } from '../../types';
2323

@@ -66,6 +66,7 @@ const PdfBlockComponent = ({
6666
const pdfUrl = block.props.url;
6767
const { i18n, t } = useTranslation();
6868
const lang = i18n.resolvedLanguage;
69+
const [isPDFContent, setIsPDFContent] = useState<boolean | null>(null);
6970

7071
useEffect(() => {
7172
if (lang && locales[lang as keyof typeof locales]) {
@@ -82,29 +83,74 @@ const PdfBlockComponent = ({
8283
}
8384
}, [lang, t]);
8485

86+
useEffect(() => {
87+
if (!pdfUrl) {
88+
setIsPDFContent(false);
89+
return;
90+
}
91+
92+
const validatePDFContent = async () => {
93+
try {
94+
const response = await fetch(pdfUrl);
95+
const contentType = response.headers.get('content-type');
96+
97+
if (response.ok && contentType?.includes('application/pdf')) {
98+
setIsPDFContent(true);
99+
} else {
100+
setIsPDFContent(false);
101+
}
102+
} catch {
103+
setIsPDFContent(false);
104+
}
105+
};
106+
107+
void validatePDFContent();
108+
}, [pdfUrl]);
109+
85110
return (
86111
<Box ref={contentRef} className="bn-file-block-content-wrapper">
87112
<PDFBlockStyle />
88-
<ResizableFileBlockWrapper
89-
buttonIcon={
90-
<Icon iconName="upload" $size="24px" $css="line-height: normal;" />
91-
}
92-
block={block as unknown as FileBlockBlock}
93-
editor={editor as unknown as FileBlockEditor}
94-
>
113+
{isPDFContent === null && <Loading />}
114+
{isPDFContent !== null && !isPDFContent && (
95115
<Box
96-
className="bn-visual-media"
97-
role="presentation"
98-
as="embed"
99-
$width="100%"
100-
$height="450px"
101-
type="application/pdf"
102-
src={pdfUrl}
116+
$align="center"
117+
$justify="center"
118+
$color="#666"
119+
$background="#f5f5f5"
120+
$border="1px solid #ddd"
121+
$height="300px"
122+
$css={css`
123+
text-align: center;
124+
`}
103125
contentEditable={false}
104-
draggable={false}
105126
onClick={() => editor.setTextCursorPosition(block)}
106-
/>
107-
</ResizableFileBlockWrapper>
127+
>
128+
{t('Invalid or missing PDF file.')}
129+
</Box>
130+
)}
131+
{isPDFContent && (
132+
<ResizableFileBlockWrapper
133+
buttonIcon={
134+
<Icon iconName="upload" $size="24px" $css="line-height: normal;" />
135+
}
136+
block={block as unknown as FileBlockBlock}
137+
editor={editor as unknown as FileBlockEditor}
138+
>
139+
<Box
140+
as="embed"
141+
className="bn-visual-media"
142+
role="presentation"
143+
$width="100%"
144+
$height="450px"
145+
type="application/pdf"
146+
src={pdfUrl}
147+
aria-label={block.props.name || t('PDF document')}
148+
contentEditable={false}
149+
draggable={false}
150+
onClick={() => editor.setTextCursorPosition(block)}
151+
/>
152+
</ResizableFileBlockWrapper>
153+
)}
108154
</Box>
109155
);
110156
};

0 commit comments

Comments
 (0)