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