diff --git a/src/components/MenuEnvelope.vue b/src/components/MenuEnvelope.vue
index a88d54b63f..38f5b269dc 100644
--- a/src/components/MenuEnvelope.vue
+++ b/src/components/MenuEnvelope.vue
@@ -160,6 +160,13 @@
{{ t('mail', 'View source') }}
+
+
+
+
+ {{ t('mail', 'Print message') }}
+
@@ -238,6 +245,7 @@ import DownloadIcon from 'vue-material-design-icons/Download.vue'
import { mailboxHasRights } from '../util/acl.js'
import { generateUrl } from '@nextcloud/router'
import InformationIcon from 'vue-material-design-icons/Information.vue'
+import PrinterIcon from 'vue-material-design-icons/Printer.vue'
import ImportantIcon from './icons/ImportantIcon.vue'
import OpenInNewIcon from 'vue-material-design-icons/OpenInNew.vue'
import PlusIcon from 'vue-material-design-icons/Plus.vue'
@@ -254,7 +262,9 @@ import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
import AlarmIcon from 'vue-material-design-icons/Alarm.vue'
import logger from '../logger.js'
import moment from '@nextcloud/moment'
+import { shortRelativeDatetime } from '../util/shortRelativeDatetime.js'
import { mapGetters } from 'vuex'
+import { translate as t } from '@nextcloud/l10n'
export default {
name: 'MenuEnvelope',
@@ -281,6 +291,7 @@ export default {
ImportantIcon,
TaskIcon,
AlarmIcon,
+ PrinterIcon,
},
props: {
envelope: {
@@ -558,6 +569,84 @@ export default {
setCustomSnooze() {
this.onSnooze(this.customSnoozeDateTime.valueOf())
},
+ async onPrint() {
+ await this.$emit('print')
+
+ const printStyles = `
+ * {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, 'Noto Color Emoji', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+ };
+ `
+
+ let combinedContent = ''
+ const messageFrames = document.querySelectorAll('iframe.message-frame')
+
+ messageFrames.forEach(frame => {
+ const doc = frame.contentDocument || frame.contentWindow.document
+ combinedContent += doc.body.innerHTML
+ })
+
+ const frame = document.createElement('iframe')
+ frame.name = 'printFrame'
+ frame.style.position = 'absolute'
+ frame.style.top = '-1000000px'
+ document.body.appendChild(frame)
+ const frameDoc = frame.contentWindow ? frame.contentWindow : frame.contentDocument.document ? frame.contentDocument.document : frame.contentDocument
+ frameDoc.document.open()
+ frameDoc.document.write('')
+ frameDoc.document.write(document.querySelector('#mail-thread-header h2').outerHTML)
+
+ if (this.envelope.from.length) {
+ let fromString = '' + t('mail', 'From') + ': '
+ this.envelope.from.forEach((contact) => {
+ fromString += ` ${this.sanitize(contact.label)} ${this.sanitize(contact.email)}`
+ })
+ frameDoc.document.write(fromString + '
')
+ }
+
+ if (this.envelope.to.length) {
+ let toString = '' + t('mail', 'Cc') + ': '
+ this.envelope.to.forEach((contact) => {
+ toString += ` ${this.sanitize(contact.label)} ${this.sanitize(contact.email)}`
+ })
+ frameDoc.document.write(toString + '
')
+ }
+
+ if (this.envelope.bcc.length) {
+ let bccString = '' + t('mail', 'Bcc') + ': '
+ this.envelope.bcc.forEach((contact) => {
+ bccString += ` ${this.sanitize(contact.label)} ${this.sanitize(contact.email)}`
+ })
+ frameDoc.document.write(bccString + '
')
+ }
+ const momentDate = moment(this.envelope.dateInt * 1000)
+
+ frameDoc.document.write(`${momentDate.format('H:mm MMM D, YYYY')}
`)
+ frameDoc.document.write(combinedContent)
+ frameDoc.document.write('')
+ frameDoc.document.close()
+
+ setTimeout(function() {
+ window.frames.printFrame.focus()
+ window.frames.printFrame.print()
+ document.body.removeChild(frame)
+ }, 500)
+ },
+
+ sanitize(string) {
+ const map = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ "/": '/',
+ };
+ const reg = /[&<>"'/]/ig;
+ return string.replace(reg, (match)=>(map[match]));
+ },
},
}
diff --git a/src/components/Thread.vue b/src/components/Thread.vue
index 96466de1a9..817bf07137 100644
--- a/src/components/Thread.vue
+++ b/src/components/Thread.vue
@@ -48,6 +48,7 @@
:expanded="expandedThreads.includes(env.databaseId)"
:full-height="thread.length === 1"
@delete="$emit('delete', env.databaseId)"
+ @print="printSetup"
@move="onMove(env.databaseId)"
@toggle-expand="toggleExpand(env.databaseId)" />
@@ -309,6 +310,9 @@ export default {
}
}
},
+ printSetup(databaseId) {
+ this.expandedThreads = [databaseId]
+ },
},
}
@@ -429,34 +433,6 @@ export default {
}
}
-@media print {
- #mail-thread-header-fields {
- position: relative;
- }
- .app-content-details,
- .splitpanes__pane-details {
- max-width: unset !important;
- width: 100% !important;
- }
- #header,
- .app-navigation,
- #reply-composer,
- #forward-button,
- #mail-message-has-blocked-content,
- .app-content-list,
- .message-composer,
- .splitpanes__pane-list,
- .mail-message-attachments {
- display: none !important;
- }
- .app-content {
- margin-left: 0 !important;
- }
- .mail-message-body {
- margin-bottom: 0 !important;
- }
-}
-
.message-source {
font-family: monospace;
white-space: pre-wrap;
diff --git a/src/components/ThreadEnvelope.vue b/src/components/ThreadEnvelope.vue
index a6e4a4a510..88111167b6 100644
--- a/src/components/ThreadEnvelope.vue
+++ b/src/components/ThreadEnvelope.vue
@@ -168,7 +168,8 @@
:with-show-source="true"
:more-actions-open.sync="moreActionsOpen"
@reply="onReply"
- @delete="$emit('delete',envelope.databaseId)"
+ @delete="$emit('delete', envelope.databaseId)"
+ @print="$emit('print', envelope.databaseId)"
@show-source-modal="onShowSourceModal"
@open-tag-modal="onOpenTagModal"
@open-move-modal="onOpenMoveModal"