Use inline content disposition for common images and PDFs (#6924)
* Use inline content disposition for common images and PDFs * Add double-click on widgets to download
This commit is contained in:
@@ -94,7 +94,9 @@ router.get(
|
||||
|
||||
ctx.set("Cache-Control", cacheHeader);
|
||||
ctx.set("Content-Type", attachment.contentType);
|
||||
ctx.attachment(attachment.name);
|
||||
ctx.attachment(attachment.name, {
|
||||
type: FileStorage.getContentDisposition(attachment.contentType),
|
||||
});
|
||||
ctx.body = attachment.stream;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,4 +214,29 @@ export default abstract class BaseStorage {
|
||||
* @returns A promise that resolves when the file is deleted
|
||||
*/
|
||||
public abstract deleteFile(key: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Returns the content disposition for a given content type.
|
||||
*
|
||||
* @param contentType The content type
|
||||
* @returns The content disposition
|
||||
*/
|
||||
public getContentDisposition(contentType?: string) {
|
||||
if (contentType && this.safeInlineContentTypes.includes(contentType)) {
|
||||
return "inline";
|
||||
}
|
||||
|
||||
return "attachment";
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of content types considered safe to display inline in the browser.
|
||||
*/
|
||||
protected safeInlineContentTypes = [
|
||||
"application/pdf",
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
];
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default class S3Storage extends BaseStorage {
|
||||
["starts-with", "$Cache-Control", ""],
|
||||
]),
|
||||
Fields: {
|
||||
"Content-Disposition": "attachment",
|
||||
"Content-Disposition": this.getContentDisposition(contentType),
|
||||
key,
|
||||
acl,
|
||||
},
|
||||
@@ -114,7 +114,7 @@ export default class S3Storage extends BaseStorage {
|
||||
Key: key,
|
||||
ContentType: contentType,
|
||||
ContentLength: contentLength,
|
||||
ContentDisposition: "attachment",
|
||||
ContentDisposition: this.getContentDisposition(contentType),
|
||||
Body: body,
|
||||
})
|
||||
.promise();
|
||||
@@ -145,7 +145,6 @@ export default class S3Storage extends BaseStorage {
|
||||
Bucket: env.AWS_S3_UPLOAD_BUCKET_NAME,
|
||||
Key: key,
|
||||
Expires: expiresIn,
|
||||
ResponseContentDisposition: "attachment",
|
||||
};
|
||||
|
||||
const url = isDocker
|
||||
|
||||
@@ -4,24 +4,38 @@ import { s } from "../../styles";
|
||||
import { sanitizeUrl } from "../../utils/urls";
|
||||
|
||||
type Props = {
|
||||
/** Icon to display on the left side of the widget */
|
||||
icon: React.ReactNode;
|
||||
/** Title of the widget */
|
||||
title: React.ReactNode;
|
||||
/** Context, displayed to right of title */
|
||||
context?: React.ReactNode;
|
||||
/** URL to open when the widget is clicked */
|
||||
href: string;
|
||||
/** Whether the widget is currently selected */
|
||||
isSelected: boolean;
|
||||
/** Children to display to the right of the context */
|
||||
children?: React.ReactNode;
|
||||
/** Callback fired when the widget is double clicked */
|
||||
onDoubleClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
/** Callback fired when the widget is clicked */
|
||||
onMouseDown?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
/** Callback fired when the widget is clicked */
|
||||
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
};
|
||||
|
||||
export default function Widget(props: Props & ThemeProps<DefaultTheme>) {
|
||||
const className = props.isSelected
|
||||
? "ProseMirror-selectednode widget"
|
||||
: "widget";
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
className={
|
||||
props.isSelected ? "ProseMirror-selectednode widget" : "widget"
|
||||
}
|
||||
className={className}
|
||||
target="_blank"
|
||||
href={sanitizeUrl(props.href)}
|
||||
rel="noreferrer nofollow"
|
||||
onDoubleClick={props.onDoubleClick}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
|
||||
@@ -86,6 +86,9 @@ export default class Attachment extends Node {
|
||||
href={node.attrs.href}
|
||||
title={node.attrs.title}
|
||||
onMouseDown={this.handleSelect(props)}
|
||||
onDoubleClick={() => {
|
||||
this.editor.commands.downloadAttachment();
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (isEditable) {
|
||||
event.preventDefault();
|
||||
@@ -159,6 +162,7 @@ export default class Attachment extends Node {
|
||||
// create a temporary link node and click it
|
||||
const link = document.createElement("a");
|
||||
link.href = node.attrs.href;
|
||||
link.target = "_blank";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user