Skip to content

Commit

Permalink
Merge pull request #301 from opcodesio/bug/mail-preview-issues
Browse files Browse the repository at this point in the history
fix mail parsing issues
  • Loading branch information
arukompas authored Nov 19, 2023
2 parents 68d9f76 + c49b05e commit 2fd7051
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 103 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"require": {
"php": "^8.0",
"illuminate/contracts": "^8.0|^9.0|^10.0",
"opcodesio/mail-parser": "^0.1.1"
"opcodesio/mail-parser": "^0.1.6"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.2",
Expand Down
2 changes: 1 addition & 1 deletion public/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/app.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"/app.js": "/app.js?id=0c6cf8ac8d5abf3598949d9417bf56eb",
"/app.css": "/app.css?id=df9446e9defec9b688bb5566dc40e285",
"/app.js": "/app.js?id=3aebfa606492fb5d84145443e7a458cd",
"/app.css": "/app.css?id=46e730db84da71f61a3f42fe6b08d8eb",
"/img/log-viewer-128.png": "/img/log-viewer-128.png?id=d576c6d2e16074d3f064e60fe4f35166",
"/img/log-viewer-32.png": "/img/log-viewer-32.png?id=f8ec67d10f996aa8baf00df3b61eea6d",
"/img/log-viewer-64.png": "/img/log-viewer-64.png?id=8902d596fc883ca9eb8105bb683568c6"
Expand Down
4 changes: 4 additions & 0 deletions resources/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ html.dark {
@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;
}

.mail-preview-text {
@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;
}

.mail-attachment-button {
@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;
max-width: 460px;
Expand Down
33 changes: 23 additions & 10 deletions resources/js/components/BaseLogTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@
</div>

<tab-container v-if="logViewerStore.isOpen(index)" :tabs="getTabsForLog(log)">
<tab-content v-if="log.extra && log.extra.mail_preview" tab-value="mail_preview">
<mail-preview :mail="log.extra.mail_preview" />
<tab-content v-if="log.extra && log.extra.mail_preview && log.extra.mail_preview.html" tab-value="mail_html_preview">
<mail-html-preview :mail="log.extra.mail_preview" />
</tab-content>

<tab-content v-if="log.extra && log.extra.mail_preview && log.extra.mail_preview.text" tab-value="mail_text_preview">
<mail-text-preview :mail="log.extra.mail_preview" />
</tab-content>

<tab-content tab-value="raw">
Expand Down Expand Up @@ -145,7 +149,8 @@ import { handleLogToggleKeyboardNavigation } from '../keyboardNavigation';
import { useSeverityStore } from '../stores/severity.js';
import TabContainer from "./TabContainer.vue";
import TabContent from "./TabContent.vue";
import MailPreview from "./MailPreview.vue";
import MailHtmlPreview from "./MailHtmlPreview.vue";
import MailTextPreview from "./MailTextPreview.vue";
const fileStore = useFileStore();
const logViewerStore = useLogViewerStore();
Expand All @@ -170,14 +175,22 @@ const hasContext = (log) => {
return log.context && Object.keys(log.context).length > 0;
}
const hasPreviews = (log) => {
return getExtraTabsForLog(log).length > 0;
}
const getExtraTabsForLog = (log) => {
return [
log.extra && log.extra.mail_preview ? { name: 'Mail preview', value: 'mail_preview' } : null,
].filter(Boolean);
let tabs = [];
if (! log.extra || ! log.extra.mail_preview) {
return tabs;
}
if (log.extra.mail_preview.html) {
tabs.push({ name: 'HTML preview', value: 'mail_html_preview' });
}
if (log.extra.mail_preview.text) {
tabs.push({ name: 'Text preview', value: 'mail_text_preview' });
}
return tabs;
}
const getTabsForLog = (log) => {
Expand Down
34 changes: 34 additions & 0 deletions resources/js/components/MailHtmlPreview.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div class="mail-preview">
<!-- headers -->
<mail-preview-attributes :mail="mail"/>

<!-- HTML preview -->
<iframe
v-if="mail.html"
class="mail-preview-html"
:style="{height: `${iframeHeight}px`}"
:srcdoc="mail.html"
@load="setIframeHeight"
ref="iframe"
></iframe>
</div>
</template>

<script setup>
import {ref} from "vue";
import MailPreviewAttributes from "./MailPreviewAttributes.vue";
defineProps({
mail: {
type: Object,
},
})
const iframe = ref(null);
const iframeHeight = ref(600);
const setIframeHeight = () => {
iframeHeight.value = (iframe.value?.contentWindow?.document?.body?.clientHeight || 580) + 20;
}
</script>
85 changes: 0 additions & 85 deletions resources/js/components/MailPreview.vue

This file was deleted.

69 changes: 69 additions & 0 deletions resources/js/components/MailPreviewAttributes.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div class="mail-preview-attributes">
<table>
<tr v-if="mail.from">
<td class="font-semibold">From</td>
<td>{{ mail.from }}</td>
</tr>
<tr v-if="mail.to">
<td class="font-semibold">To</td>
<td>{{ mail.to }}</td>
</tr>
<tr v-if="mail.id">
<td class="font-semibold">Message ID</td>
<td>{{ mail.id }}</td>
</tr>
<tr v-if="mail.subject">
<td class="font-semibold">Subject</td>
<td>{{ mail.subject }}</td>
</tr>
<tr v-if="mail.attachments && mail.attachments.length > 0">
<td class="font-semibold">Attachments</td>
<td>
<div v-for="(attachment, index) in mail.attachments" :key="`mail-${mail.id}-attachment-${index}`"
class="mail-attachment-button"
>
<div class="flex items-center">
<PaperClipIcon class="h-4 w-4 text-gray-500 dark:text-gray-400 mr-1"/>
<span>{{ attachment.filename }} <span class="opacity-60">({{ attachment.size_formatted }})</span></span>
</div>
<div>
<a href="#" @click.prevent="downloadAttachment(attachment)"
class="text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400"
>Download</a>
</div>
</div>
</td>
</tr>
</table>
</div>
</template>

<script setup>
import { PaperClipIcon } from '@heroicons/vue/24/outline';
defineProps(['mail']);
const downloadAttachment = (attachment) => {
// Decode the base64 encoded string
const decodedContent = atob(attachment.content);
// Convert decoded base64 string to a Uint8Array
const byteNumbers = new Array(decodedContent.length);
for (let i = 0; i < decodedContent.length; i++) {
byteNumbers[i] = decodedContent.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: attachment.content_type || 'application/octet-stream' });
const blobUrl = URL.createObjectURL(blob);
const downloadLink = document.createElement('a');
downloadLink.href = blobUrl;
downloadLink.download = attachment.filename;
downloadLink.click();
// Clean up the temporary URL after the download
URL.revokeObjectURL(blobUrl);
}
</script>
31 changes: 31 additions & 0 deletions resources/js/components/MailTextPreview.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<div class="mail-preview">
<!-- headers -->
<mail-preview-attributes :mail="mail"/>

<!-- Text preview -->
<pre
v-if="mail.text"
class="mail-preview-text"
v-text="mail.text"
></pre>
</div>
</template>

<script setup>
import {ref} from "vue";
import MailPreviewAttributes from "./MailPreviewAttributes.vue";
defineProps({
mail: {
type: Object,
},
})
const iframe = ref(null);
const iframeHeight = ref(600);
const setIframeHeight = () => {
iframeHeight.value = (iframe.value?.contentWindow?.document?.body?.clientHeight || 580) + 20;
}
</script>
2 changes: 1 addition & 1 deletion src/Logs/LaravelLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ protected function extractMailPreview(): void
'from' => $message->getFrom(),
'to' => $message->getTo(),
'attachments' => array_map(fn ($attachment) => [
'content' => $attachment->getContent(),
'content' => base64_encode($attachment->getContent()),
'content_type' => $attachment->getContentType(),
'filename' => $attachment->getFilename(),
'size_formatted' => Utils::bytesForHumans($attachment->getSize()),
Expand Down
4 changes: 2 additions & 2 deletions tests/Unit/LaravelLogs/LaravelLogsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
Subject: This is an email with common headers
Date: Thu, 24 Aug 2023 21:15:01 PST
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_Part_1_1234567890"
Content-Type: multipart/alternative; boundary="----=_Part_1_1234567890"
------=_Part_1_1234567890
Content-Type: text/plain; charset="utf-8"
Expand Down Expand Up @@ -52,7 +52,7 @@
'to' => 'recipient@example.com',
'attachments' => [
[
'content' => 'Example attachment content',
'content' => base64_encode('Example attachment content'),
'content_type' => 'text/plain; charset="utf-8"',
'filename' => 'example.txt',
'size_formatted' => Utils::bytesForHumans(strlen('Example attachment content')),
Expand Down

0 comments on commit 2fd7051

Please sign in to comment.