diff --git a/paperless-backend/src/inbox/inbox.controller.ts b/paperless-backend/src/inbox/inbox.controller.ts index 48bf81b..068989e 100644 --- a/paperless-backend/src/inbox/inbox.controller.ts +++ b/paperless-backend/src/inbox/inbox.controller.ts @@ -200,7 +200,7 @@ export class InboxController { subject: string; body: string; html?: string; - filename?: string; + segments: { pages: number[]; filename: string }[]; }, @Request() req: any, ): Promise { diff --git a/paperless-backend/src/inbox/inbox.service.ts b/paperless-backend/src/inbox/inbox.service.ts index 5f64788..2e6b63c 100644 --- a/paperless-backend/src/inbox/inbox.service.ts +++ b/paperless-backend/src/inbox/inbox.service.ts @@ -15,7 +15,7 @@ import { type InboxSource, } from '../database/entities/inbox-document.entity'; import { MailService } from '../postprocessing/mail.service'; -import { applyEditsToTemp, cleanupTemp, buildSegmentBuffer } from '../inbox-postprocessor/edit-applier'; +import { buildSegmentBuffer } from '../inbox-postprocessor/edit-applier'; export interface InboxFile { id: string; @@ -273,24 +273,27 @@ export class InboxService { subject: string; body: string; html?: string; - filename?: string; + segments: { pages: number[]; filename: string }[]; }, ): Promise { const { doc, pdfPath } = await this.resolveDocument(id, preferredUsername); - let tmpPath: string | null = null; - try { - tmpPath = await applyEditsToTemp(doc, pdfPath); - const content = await fs.readFile(tmpPath); - const filename = opts.filename ? `${opts.filename}.pdf` : doc.OriginalName; - await this.mailService.sendMail({ - to: opts.to, - subject: opts.subject, - body: opts.body, - html: opts.html, - attachments: [{ filename, content }], - }); - } finally { - await cleanupTemp(tmpPath); - } + const deleted = new Set(doc.DeletedPages ?? []); + + const attachments = await Promise.all( + opts.segments.map(async (seg) => { + const safePages = seg.pages.filter((p) => p >= 1 && p <= doc.PageCount && !deleted.has(p)); + const content = await buildSegmentBuffer(doc, pdfPath, safePages); + const filename = seg.filename.endsWith('.pdf') ? seg.filename : `${seg.filename}.pdf`; + return { filename, content }; + }), + ); + + await this.mailService.sendMail({ + to: opts.to, + subject: opts.subject, + body: opts.body, + html: opts.html, + attachments, + }); } } diff --git a/paperless-frontend/index.html b/paperless-frontend/index.html index 267a860..9397188 100644 --- a/paperless-frontend/index.html +++ b/paperless-frontend/index.html @@ -3,6 +3,10 @@ + + + + diff --git a/paperless-frontend/public/apple-touch-icon-precomposed.png b/paperless-frontend/public/apple-touch-icon-precomposed.png new file mode 100644 index 0000000..51d9158 Binary files /dev/null and b/paperless-frontend/public/apple-touch-icon-precomposed.png differ diff --git a/paperless-frontend/public/apple-touch-icon.png b/paperless-frontend/public/apple-touch-icon.png new file mode 100644 index 0000000..51d9158 Binary files /dev/null and b/paperless-frontend/public/apple-touch-icon.png differ diff --git a/paperless-frontend/src/api/inbox.ts b/paperless-frontend/src/api/inbox.ts index df45897..5f9a238 100644 --- a/paperless-frontend/src/api/inbox.ts +++ b/paperless-frontend/src/api/inbox.ts @@ -119,7 +119,7 @@ export const inboxApi = { subject: string; body: string; html?: string; - filename?: string; + segments: { pages: number[]; filename: string }[]; }, ) => api.post(`/api/inbox/${encodeURIComponent(id)}/send-email`, body).then(() => {}), diff --git a/paperless-frontend/src/pages/InboxDetailPage.tsx b/paperless-frontend/src/pages/InboxDetailPage.tsx index c7e5df5..bb7f89c 100644 --- a/paperless-frontend/src/pages/InboxDetailPage.tsx +++ b/paperless-frontend/src/pages/InboxDetailPage.tsx @@ -660,13 +660,15 @@ function TiptapToolbar({ editor }: { editor: ReturnType }) { interface SendEmailDialogProps { open: boolean; fileId: string; - defaultFilename: string; + fileName: string; + documents: DocumentSegment[]; onClose: () => void; } -function SendEmailDialog({ open, fileId, defaultFilename, onClose }: SendEmailDialogProps) { +function SendEmailDialog({ open, fileId, fileName, documents, onClose }: SendEmailDialogProps) { const [form] = Form.useForm(); const [submitting, setSubmitting] = useState(false); + const [filenames, setFilenames] = useState([]); const editor = useEditor({ extensions: [StarterKit, Underline], @@ -674,12 +676,16 @@ function SendEmailDialog({ open, fileId, defaultFilename, onClose }: SendEmailDi }); useEffect(() => { - if (open) { - form.resetFields(); - form.setFieldsValue({ filename: defaultFilename }); - editor?.commands.clearContent(); - } - }, [open, defaultFilename, form, editor]); + if (!open) return; + form.resetFields(); + editor?.commands.clearContent(); + const base = fileName.replace(/\.pdf$/i, ''); + setFilenames( + documents.map((doc) => + doc.belegname || (documents.length === 1 ? base : `${base}_${doc.index + 1}`), + ), + ); + }, [open, documents, fileName, form, editor]); const handleOk = async () => { try { @@ -690,7 +696,10 @@ function SendEmailDialog({ open, fileId, defaultFilename, onClose }: SendEmailDi subject: values.subject, body: editor?.getText() ?? '', html: editor?.getHTML(), - filename: values.filename || undefined, + segments: documents.map((doc, i) => ({ + pages: doc.pages, + filename: filenames[i] || fileName, + })), }); message.success('E-Mail wurde gesendet'); onClose(); @@ -727,8 +736,26 @@ function SendEmailDialog({ open, fileId, defaultFilename, onClose }: SendEmailDi - - + +
+ {documents.map((doc, i) => { + const first = doc.pages[0]; + const last = doc.pages[doc.pages.length - 1]; + const range = first === last ? `Seite ${first}` : `Seiten ${first}–${last}`; + return ( +
+ {range} + + setFilenames((prev) => prev.map((f, j) => (j === i ? e.target.value : f))) + } + suffix=".pdf" + /> +
+ ); + })} +
@@ -1560,7 +1587,8 @@ export default function InboxDetailPage() { setEmailDialogOpen(false)} />