Skip to content

Commit 2fd7051

Browse files
authored
Merge pull request opcodesio#301 from opcodesio/bug/mail-preview-issues
fix mail parsing issues
2 parents 68d9f76 + c49b05e commit 2fd7051

File tree

12 files changed

+169
-103
lines changed

12 files changed

+169
-103
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"require": {
2222
"php": "^8.0",
2323
"illuminate/contracts": "^8.0|^9.0|^10.0",
24-
"opcodesio/mail-parser": "^0.1.1"
24+
"opcodesio/mail-parser": "^0.1.6"
2525
},
2626
"require-dev": {
2727
"guzzlehttp/guzzle": "^7.2",

public/app.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/app.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/mix-manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"/app.js": "/app.js?id=0c6cf8ac8d5abf3598949d9417bf56eb",
3-
"/app.css": "/app.css?id=df9446e9defec9b688bb5566dc40e285",
2+
"/app.js": "/app.js?id=3aebfa606492fb5d84145443e7a458cd",
3+
"/app.css": "/app.css?id=46e730db84da71f61a3f42fe6b08d8eb",
44
"/img/log-viewer-128.png": "/img/log-viewer-128.png?id=d576c6d2e16074d3f064e60fe4f35166",
55
"/img/log-viewer-32.png": "/img/log-viewer-32.png?id=f8ec67d10f996aa8baf00df3b61eea6d",
66
"/img/log-viewer-64.png": "/img/log-viewer-64.png?id=8902d596fc883ca9eb8105bb683568c6"

resources/css/app.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ html.dark {
366366
@apply w-full border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-900 overflow-auto mb-4 lg:mb-6;
367367
}
368368

369+
.mail-preview-text {
370+
@apply w-full border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-900 text-sm whitespace-pre-wrap mb-4 lg:mb-6 p-4;
371+
}
372+
369373
.mail-attachment-button {
370374
@apply flex items-center justify-between px-2 py-1 lg:px-4 lg:py-2 bg-white dark:bg-gray-800 border dark:border-gray-700 rounded;
371375
max-width: 460px;

resources/js/components/BaseLogTable.vue

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,12 @@
7979
</div>
8080

8181
<tab-container v-if="logViewerStore.isOpen(index)" :tabs="getTabsForLog(log)">
82-
<tab-content v-if="log.extra && log.extra.mail_preview" tab-value="mail_preview">
83-
<mail-preview :mail="log.extra.mail_preview" />
82+
<tab-content v-if="log.extra && log.extra.mail_preview && log.extra.mail_preview.html" tab-value="mail_html_preview">
83+
<mail-html-preview :mail="log.extra.mail_preview" />
84+
</tab-content>
85+
86+
<tab-content v-if="log.extra && log.extra.mail_preview && log.extra.mail_preview.text" tab-value="mail_text_preview">
87+
<mail-text-preview :mail="log.extra.mail_preview" />
8488
</tab-content>
8589

8690
<tab-content tab-value="raw">
@@ -145,7 +149,8 @@ import { handleLogToggleKeyboardNavigation } from '../keyboardNavigation';
145149
import { useSeverityStore } from '../stores/severity.js';
146150
import TabContainer from "./TabContainer.vue";
147151
import TabContent from "./TabContent.vue";
148-
import MailPreview from "./MailPreview.vue";
152+
import MailHtmlPreview from "./MailHtmlPreview.vue";
153+
import MailTextPreview from "./MailTextPreview.vue";
149154
150155
const fileStore = useFileStore();
151156
const logViewerStore = useLogViewerStore();
@@ -170,14 +175,22 @@ const hasContext = (log) => {
170175
return log.context && Object.keys(log.context).length > 0;
171176
}
172177
173-
const hasPreviews = (log) => {
174-
return getExtraTabsForLog(log).length > 0;
175-
}
176-
177178
const getExtraTabsForLog = (log) => {
178-
return [
179-
log.extra && log.extra.mail_preview ? { name: 'Mail preview', value: 'mail_preview' } : null,
180-
].filter(Boolean);
179+
let tabs = [];
180+
181+
if (! log.extra || ! log.extra.mail_preview) {
182+
return tabs;
183+
}
184+
185+
if (log.extra.mail_preview.html) {
186+
tabs.push({ name: 'HTML preview', value: 'mail_html_preview' });
187+
}
188+
189+
if (log.extra.mail_preview.text) {
190+
tabs.push({ name: 'Text preview', value: 'mail_text_preview' });
191+
}
192+
193+
return tabs;
181194
}
182195
183196
const getTabsForLog = (log) => {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<div class="mail-preview">
3+
<!-- headers -->
4+
<mail-preview-attributes :mail="mail"/>
5+
6+
<!-- HTML preview -->
7+
<iframe
8+
v-if="mail.html"
9+
class="mail-preview-html"
10+
:style="{height: `${iframeHeight}px`}"
11+
:srcdoc="mail.html"
12+
@load="setIframeHeight"
13+
ref="iframe"
14+
></iframe>
15+
</div>
16+
</template>
17+
18+
<script setup>
19+
import {ref} from "vue";
20+
import MailPreviewAttributes from "./MailPreviewAttributes.vue";
21+
22+
defineProps({
23+
mail: {
24+
type: Object,
25+
},
26+
})
27+
28+
const iframe = ref(null);
29+
const iframeHeight = ref(600);
30+
31+
const setIframeHeight = () => {
32+
iframeHeight.value = (iframe.value?.contentWindow?.document?.body?.clientHeight || 580) + 20;
33+
}
34+
</script>

resources/js/components/MailPreview.vue

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<template>
2+
<div class="mail-preview-attributes">
3+
<table>
4+
<tr v-if="mail.from">
5+
<td class="font-semibold">From</td>
6+
<td>{{ mail.from }}</td>
7+
</tr>
8+
<tr v-if="mail.to">
9+
<td class="font-semibold">To</td>
10+
<td>{{ mail.to }}</td>
11+
</tr>
12+
<tr v-if="mail.id">
13+
<td class="font-semibold">Message ID</td>
14+
<td>{{ mail.id }}</td>
15+
</tr>
16+
<tr v-if="mail.subject">
17+
<td class="font-semibold">Subject</td>
18+
<td>{{ mail.subject }}</td>
19+
</tr>
20+
<tr v-if="mail.attachments && mail.attachments.length > 0">
21+
<td class="font-semibold">Attachments</td>
22+
<td>
23+
<div v-for="(attachment, index) in mail.attachments" :key="`mail-${mail.id}-attachment-${index}`"
24+
class="mail-attachment-button"
25+
>
26+
<div class="flex items-center">
27+
<PaperClipIcon class="h-4 w-4 text-gray-500 dark:text-gray-400 mr-1"/>
28+
<span>{{ attachment.filename }} <span class="opacity-60">({{ attachment.size_formatted }})</span></span>
29+
</div>
30+
<div>
31+
<a href="#" @click.prevent="downloadAttachment(attachment)"
32+
class="text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400"
33+
>Download</a>
34+
</div>
35+
</div>
36+
</td>
37+
</tr>
38+
</table>
39+
</div>
40+
</template>
41+
42+
<script setup>
43+
import { PaperClipIcon } from '@heroicons/vue/24/outline';
44+
45+
defineProps(['mail']);
46+
47+
const downloadAttachment = (attachment) => {
48+
// Decode the base64 encoded string
49+
const decodedContent = atob(attachment.content);
50+
51+
// Convert decoded base64 string to a Uint8Array
52+
const byteNumbers = new Array(decodedContent.length);
53+
for (let i = 0; i < decodedContent.length; i++) {
54+
byteNumbers[i] = decodedContent.charCodeAt(i);
55+
}
56+
const byteArray = new Uint8Array(byteNumbers);
57+
58+
const blob = new Blob([byteArray], { type: attachment.content_type || 'application/octet-stream' });
59+
const blobUrl = URL.createObjectURL(blob);
60+
61+
const downloadLink = document.createElement('a');
62+
downloadLink.href = blobUrl;
63+
downloadLink.download = attachment.filename;
64+
downloadLink.click();
65+
66+
// Clean up the temporary URL after the download
67+
URL.revokeObjectURL(blobUrl);
68+
}
69+
</script>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<div class="mail-preview">
3+
<!-- headers -->
4+
<mail-preview-attributes :mail="mail"/>
5+
6+
<!-- Text preview -->
7+
<pre
8+
v-if="mail.text"
9+
class="mail-preview-text"
10+
v-text="mail.text"
11+
></pre>
12+
</div>
13+
</template>
14+
15+
<script setup>
16+
import {ref} from "vue";
17+
import MailPreviewAttributes from "./MailPreviewAttributes.vue";
18+
19+
defineProps({
20+
mail: {
21+
type: Object,
22+
},
23+
})
24+
25+
const iframe = ref(null);
26+
const iframeHeight = ref(600);
27+
28+
const setIframeHeight = () => {
29+
iframeHeight.value = (iframe.value?.contentWindow?.document?.body?.clientHeight || 580) + 20;
30+
}
31+
</script>

0 commit comments

Comments
 (0)