Skip to content

Commit

Permalink
Use inline content disposition for common images and PDFs (#6924)
Browse files Browse the repository at this point in the history
* Use inline content disposition for common images and PDFs

* Add double-click on widgets to download
  • Loading branch information
tommoor committed May 18, 2024
1 parent c872f3e commit cd4f3f9
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 7 deletions.
4 changes: 3 additions & 1 deletion plugins/storage/server/api/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
25 changes: 25 additions & 0 deletions server/storage/files/BaseStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
];
}
5 changes: 2 additions & 3 deletions server/storage/files/S3Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
20 changes: 17 additions & 3 deletions shared/editor/components/Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
>
Expand Down
4 changes: 4 additions & 0 deletions shared/editor/nodes/Attachment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();

Expand Down

0 comments on commit cd4f3f9

Please sign in to comment.