Wrap markdown rendering in iframe (#837)

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
Tilman Vatteroth 2021-01-24 20:50:51 +01:00 committed by GitHub
parent bd31076928
commit 586969f368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1014 additions and 287 deletions

View file

@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import { ReactElement } from 'react'
import { ComponentReplacer, SubNodeTransform } from '../ComponentReplacer'
export class LinkInNewTabReplacer extends ComponentReplacer {
public getReplacement (node: DomElement, subNodeTransform: SubNodeTransform): (ReactElement | null | undefined) {
const isJumpMark = node.attribs?.href?.substr(0, 1) === '#'
if (node.name !== 'a' || isJumpMark) {
return undefined
}
return <a className={node.attribs?.class} title={node.attribs?.title} href={node.attribs?.href} rel='noopener noreferrer' target='_blank'>
{
node.children?.map((child, index) => subNodeTransform(child, index))
}
</a>
}
}

View file

@ -1,8 +1,8 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, ReactElement, useEffect, useState } from 'react'
import ReactHtmlParser from 'react-html-parser'
@ -59,7 +59,7 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
{ dom }
</code>
<div className={'text-right button-inside'}>
<CopyToClipboardButton content={code}/>
<CopyToClipboardButton content={code} data-cy="copy-code-button"/>
</div>
</Fragment>)
}

View file

@ -8,6 +8,7 @@ import React from 'react'
import { Modal } from 'react-bootstrap'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import "./lightbox.scss"
import { ProxyImageFrame } from './proxy-image-frame'
export interface ImageLightboxModalProps {
show: boolean
@ -33,7 +34,7 @@ export const ImageLightboxModal: React.FC<ImageLightboxModalProps> = ({ show, on
<span>{alt ?? title ?? ''}</span>
</Modal.Title>
</Modal.Header>
<img alt={alt} src={src} title={title} className={'w-100 cursor-zoom-out'} onClick={onHide}/>
<ProxyImageFrame alt={alt} src={src} title={title} className={'w-100 cursor-zoom-out'} onClick={onHide}/>
</Modal>
)
}

View file

@ -9,17 +9,27 @@ import React from 'react'
import { ComponentReplacer } from '../ComponentReplacer'
import { ProxyImageFrame } from './proxy-image-frame'
export type ImageClickHandler = (event: React.MouseEvent<HTMLImageElement, MouseEvent>) => void;
export class ImageReplacer extends ComponentReplacer {
private readonly clickHandler?: ImageClickHandler
constructor (clickHandler?: ImageClickHandler) {
super()
this.clickHandler = clickHandler
}
public getReplacement (node: DomElement): React.ReactElement | undefined {
if (node.name === 'img' && node.attribs) {
return <ProxyImageFrame
id={node.attribs.id}
className={node.attribs.class}
className={`${node.attribs.class} cursor-zoom-in`}
src={node.attribs.src}
alt={node.attribs.alt}
title={node.attribs.title}
width={node.attribs.width}
height={node.attribs.height}
onClick={this.clickHandler}
/>
}
}

View file

@ -1,29 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useState } from 'react'
import { ImageLightboxModal } from './image-lightbox-modal'
import "./lightbox.scss"
export const LightboxImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>> = (
{
alt,
title,
src,
...props
}) => {
const [showFullscreenImage, setShowFullscreenImage] = useState(false)
return (
<Fragment>
<img alt={alt} src={src} title={title} {...props} className={'cursor-zoom-in'}
onClick={() => setShowFullscreenImage(true)}/>
<ImageLightboxModal
show={showFullscreenImage}
onHide={() => setShowFullscreenImage(false)} title={title} src={src} alt={alt}/>
</Fragment>
)
}

View file

@ -8,7 +8,6 @@ import React, { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { getProxiedUrl } from '../../../../api/media'
import { ApplicationState } from '../../../../redux'
import { LightboxImageFrame } from './lightbox-image-frame'
export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>> = (
{
@ -29,13 +28,6 @@ export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>
.catch(err => console.error(err))
}, [imageProxyEnabled, src])
if (imageProxyEnabled) {
return (
<LightboxImageFrame src={imageUrl} title={title ?? alt ?? ''} alt={alt} {...props}/>
)
}
return (
<LightboxImageFrame src={src ?? ''} title={title ?? alt ?? ''} alt={alt} {...props}/>
)
return <img src={imageProxyEnabled ? imageUrl : (src ?? '')} title={title ?? alt ?? ''} alt={alt} {...props}/>
}

View file

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { DomElement } from 'domhandler'
import React, { ReactElement } from 'react'
import { ComponentReplacer, NativeRenderer, SubNodeTransform } from '../ComponentReplacer'
export const createJumpToMarkClickEventHandler = (id: string) => {
return (event: React.MouseEvent<HTMLElement, MouseEvent>): void => {
document.getElementById(id)?.scrollIntoView()
event.preventDefault()
}
}
export class LinkReplacer extends ComponentReplacer {
constructor (private baseUrl?: string) {
super()
}
public getReplacement (node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): (ReactElement | null | undefined) {
if (node.name !== 'a' || !node.attribs || !node.attribs.href) {
return undefined
}
const url = node.attribs.href
const isJumpMark = url.substr(0, 1) === '#'
const id = url.substr(1)
try {
node.attribs.href = new URL(url, this.baseUrl).toString()
} catch (e) {
node.attribs.href = url
}
if (isJumpMark) {
return <span onClick={createJumpToMarkClickEventHandler(id)}>
{nativeRenderer()}
</span>
} else {
node.attribs.rel = "noreferer noopener"
node.attribs.target = "_blank"
return nativeRenderer()
}
}
}