mirror of
https://github.com/axllent/mailpit.git
synced 2025-05-13 22:06:31 +02:00
UI: HTML source & highlighting
This commit is contained in:
parent
d4e520772e
commit
fea733a43e
@ -1,3 +1,4 @@
|
|||||||
|
// Package data contains the message & mailbox structs
|
||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -19,6 +20,7 @@ type Message struct {
|
|||||||
Date time.Time
|
Date time.Time
|
||||||
Text string
|
Text string
|
||||||
HTML string
|
HTML string
|
||||||
|
HTMLSource string
|
||||||
Size int
|
Size int
|
||||||
Inline []Attachment
|
Inline []Attachment
|
||||||
Attachments []Attachment
|
Attachments []Attachment
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"bootstrap": "^5.2.0",
|
"bootstrap": "^5.2.0",
|
||||||
"bootstrap-icons": "^1.9.1",
|
"bootstrap-icons": "^1.9.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.2.13"
|
"vue": "^3.2.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -1044,6 +1045,14 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prismjs": {
|
||||||
|
"version": "1.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||||
|
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@ -1838,6 +1847,11 @@
|
|||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"prismjs": {
|
||||||
|
"version": "1.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||||
|
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q=="
|
||||||
|
},
|
||||||
"readdirp": {
|
"readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"bootstrap": "^5.2.0",
|
"bootstrap": "^5.2.0",
|
||||||
"bootstrap-icons": "^1.9.1",
|
"bootstrap-icons": "^1.9.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.2.13"
|
"vue": "^3.2.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -121,8 +121,8 @@ func apiDownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, _ = w.Write(a.Content)
|
_, _ = w.Write(a.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// View the full email source as plain text
|
// Download the full email source as plain text
|
||||||
func apiDownloadSource(w http.ResponseWriter, r *http.Request) {
|
func apiDownloadRaw(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
@ -43,7 +43,7 @@ func Listen() {
|
|||||||
r.HandleFunc("/api/read", apiMarkAllRead).Methods("GET")
|
r.HandleFunc("/api/read", apiMarkAllRead).Methods("GET")
|
||||||
r.HandleFunc("/api/read", apiMarkSelectedRead).Methods("POST")
|
r.HandleFunc("/api/read", apiMarkSelectedRead).Methods("POST")
|
||||||
r.HandleFunc("/api/unread", apiMarkSelectedUnread).Methods("POST")
|
r.HandleFunc("/api/unread", apiMarkSelectedUnread).Methods("POST")
|
||||||
r.HandleFunc("/api/{id}/source", middleWareFunc(apiDownloadSource)).Methods("GET")
|
r.HandleFunc("/api/{id}/raw", middleWareFunc(apiDownloadRaw)).Methods("GET")
|
||||||
r.HandleFunc("/api/{id}/part/{partID}", middleWareFunc(apiDownloadAttachment)).Methods("GET")
|
r.HandleFunc("/api/{id}/part/{partID}", middleWareFunc(apiDownloadAttachment)).Methods("GET")
|
||||||
r.HandleFunc("/api/{id}/delete", middleWareFunc(apiDeleteOne)).Methods("GET")
|
r.HandleFunc("/api/{id}/delete", middleWareFunc(apiDeleteOne)).Methods("GET")
|
||||||
r.HandleFunc("/api/{id}/unread", middleWareFunc(apiUnreadOne)).Methods("GET")
|
r.HandleFunc("/api/{id}/unread", middleWareFunc(apiUnreadOne)).Methods("GET")
|
||||||
|
@ -171,7 +171,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.message = d;
|
self.message = d;
|
||||||
|
|
||||||
// generate the prev/next links based on current message list
|
// generate the prev/next links based on current message list
|
||||||
self.messagePrev = false;
|
self.messagePrev = false;
|
||||||
self.messageNext = false;
|
self.messageNext = false;
|
||||||
@ -433,10 +432,10 @@ export default {
|
|||||||
<button class="btn btn-outline-secondary me-2" title="Mark unread" v-on:click="markUnread">
|
<button class="btn btn-outline-secondary me-2" title="Mark unread" v-on:click="markUnread">
|
||||||
<i class="bi bi-eye-slash"></i> <span class="d-none d-md-inline">Mark unread</span>
|
<i class="bi bi-eye-slash"></i> <span class="d-none d-md-inline">Mark unread</span>
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-outline-secondary float-end" :class="messageNext ? '':'disabled'" v-on:click="message=false" :href="'#'+messageNext" title="View next message">
|
<a class="btn btn-outline-secondary float-end" :class="messageNext ? '':'disabled'" :href="'#'+messageNext" title="View next message">
|
||||||
<i class="bi bi-caret-right-fill"></i>
|
<i class="bi bi-caret-right-fill"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-outline-secondary ms-2 me-1 float-end" :class="messagePrev ? '': 'disabled'" v-on:click="message=false" :href="'#'+messagePrev" title="View previous message">
|
<a class="btn btn-outline-secondary ms-2 me-1 float-end" :class="messagePrev ? '': 'disabled'" :href="'#'+messagePrev" title="View previous message">
|
||||||
<i class="bi bi-caret-left-fill"></i>
|
<i class="bi bi-caret-left-fill"></i>
|
||||||
</a>
|
</a>
|
||||||
<a :href="'api/' + message.ID + '/source?dl=1'" class="btn btn-outline-secondary me-2 float-end" title="Download message">
|
<a :href="'api/' + message.ID + '/source?dl=1'" class="btn btn-outline-secondary me-2 float-end" title="Download message">
|
||||||
|
@ -95,3 +95,152 @@ body.blur {
|
|||||||
filter: blur(3px);
|
filter: blur(3px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PrismJS 1.29.0 - modified!
|
||||||
|
https://prismjs.com/download.html#themes=prism-coy&languages=markup+css */
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: #000;
|
||||||
|
background: 0 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
pre[class*="language-"] {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
pre[class*="language-"] > code {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
code[class*="language-"] {
|
||||||
|
max-height: inherit;
|
||||||
|
height: inherit;
|
||||||
|
padding: 0 1em;
|
||||||
|
display: block;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
:not(pre) > code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background-color: #fdfdfd;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
position: relative;
|
||||||
|
padding: 0.2em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
color: #c92c2c;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
display: inline;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.block-comment,
|
||||||
|
.token.cdata,
|
||||||
|
.token.comment,
|
||||||
|
.token.doctype,
|
||||||
|
.token.prolog {
|
||||||
|
color: #7d8b99;
|
||||||
|
}
|
||||||
|
.token.punctuation {
|
||||||
|
color: #5f6364;
|
||||||
|
}
|
||||||
|
.token.boolean,
|
||||||
|
.token.constant,
|
||||||
|
.token.deleted,
|
||||||
|
.token.function-name,
|
||||||
|
.token.number,
|
||||||
|
.token.property,
|
||||||
|
.token.symbol,
|
||||||
|
.token.tag {
|
||||||
|
color: #c92c2c;
|
||||||
|
}
|
||||||
|
.token.attr-name,
|
||||||
|
.token.builtin,
|
||||||
|
.token.char,
|
||||||
|
.token.function,
|
||||||
|
.token.inserted,
|
||||||
|
.token.selector,
|
||||||
|
.token.string {
|
||||||
|
color: #2f9c0a;
|
||||||
|
}
|
||||||
|
.token.entity,
|
||||||
|
.token.operator,
|
||||||
|
.token.url,
|
||||||
|
.token.variable {
|
||||||
|
color: #a67f59;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.class-name,
|
||||||
|
.token.keyword {
|
||||||
|
color: #1990b8;
|
||||||
|
}
|
||||||
|
.token.important,
|
||||||
|
.token.regex {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
.language-css .token.string,
|
||||||
|
.style .token.string {
|
||||||
|
color: #a67f59;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
.token.important {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.token.bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
.token.namespace {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
pre[class*="language-"]:after,
|
||||||
|
pre[class*="language-"]:before {
|
||||||
|
bottom: 14px;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pre[class*="language-"].line-numbers.line-numbers {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
pre[class*="language-"].line-numbers.line-numbers code {
|
||||||
|
padding-left: 3.8em;
|
||||||
|
}
|
||||||
|
pre[class*="language-"].line-numbers.line-numbers .line-numbers-rows {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
pre[class*="language-"][data-line] {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
pre[data-line] code {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 4em;
|
||||||
|
}
|
||||||
|
pre .line-highlight {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import commonMixins from '../mixins.js';
|
import commonMixins from '../mixins.js';
|
||||||
import moment from 'moment'
|
import moment from 'moment';
|
||||||
|
import Prism from "prismjs";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
message: Object
|
message: Object
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [commonMixins],
|
mixins: [commonMixins],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
srcURI: false,
|
srcURI: false,
|
||||||
@ -15,14 +18,42 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
message: {
|
||||||
|
handler(newQuestion) {
|
||||||
|
let self = this;
|
||||||
|
// delay 100ms to select first tab and add HTML highlighting (prev/next)
|
||||||
|
window.setTimeout(function() {
|
||||||
|
self.renderUI();
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
// force eager callback execution
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
var self = this;
|
let self = this;
|
||||||
|
|
||||||
window.addEventListener("resize", self.resizeIframes);
|
window.addEventListener("resize", self.resizeIframes);
|
||||||
|
self.renderUI();
|
||||||
|
var tabEl = document.getElementById('nav-raw-tab');
|
||||||
|
tabEl.addEventListener('shown.bs.tab', function (event) {
|
||||||
|
self.srcURI = 'api/' + self.message.ID + '/raw';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted: function() {
|
||||||
|
window.removeEventListener("resize", this.resizeIframes);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
renderUI: function() {
|
||||||
|
let self = this;
|
||||||
// click the first non-disabled tab
|
// click the first non-disabled tab
|
||||||
document.querySelector('#nav-tab button:not([disabled])').click();
|
document.querySelector('#nav-tab button:not([disabled])').click();
|
||||||
document.activeElement.blur(); // blur focus
|
document.activeElement.blur(); // blur focus
|
||||||
|
document.getElementById('message-view').scrollTop = 0;
|
||||||
|
|
||||||
window.setTimeout(function(){
|
window.setTimeout(function(){
|
||||||
let p = document.getElementById('preview-html');
|
let p = document.getElementById('preview-html');
|
||||||
@ -42,17 +73,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
var tabEl = document.getElementById('nav-source-tab');
|
// html highlighting
|
||||||
tabEl.addEventListener('shown.bs.tab', function (event) {
|
window.Prism = window.Prism || {};
|
||||||
self.srcURI = 'api/' + self.message.ID + '/source';
|
window.Prism.manual = true;
|
||||||
});
|
Prism.highlightAll();
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted: function() {
|
|
||||||
window.removeEventListener("resize", this.resizeIframes);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
resizeIframe: function(el) {
|
resizeIframe: function(el) {
|
||||||
let i = el.target;
|
let i = el.target;
|
||||||
i.style.height = i.contentWindow.document.body.scrollHeight + 50 + 'px';
|
i.style.height = i.contentWindow.document.body.scrollHeight + 50 + 'px';
|
||||||
@ -84,6 +110,7 @@ export default {
|
|||||||
|
|
||||||
return a.length ? a : false;
|
return a.length ? a : false;
|
||||||
},
|
},
|
||||||
|
|
||||||
messageDate: function(d) {
|
messageDate: function(d) {
|
||||||
return moment(d).format('ddd, D MMM YYYY, h:mm a');
|
return moment(d).format('ddd, D MMM YYYY, h:mm a');
|
||||||
}
|
}
|
||||||
@ -92,7 +119,9 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="message" class="mh-100" style="overflow-y: scroll;">
|
<div v-if="message" id="message-view" class="mh-100" style="overflow-y: scroll;">
|
||||||
|
<div class="row w-100">
|
||||||
|
<div class="col-md">
|
||||||
<table class="messageHeaders">
|
<table class="messageHeaders">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="small">
|
<tr class="small">
|
||||||
@ -141,25 +170,42 @@ export default {
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-auto text-md-end mt-md-3">
|
||||||
|
<p class="text-muted small"><small>{{ messageDate(message.Date) }}</small></p>
|
||||||
|
<div class="dropdown" v-if="allAttachments(message)">
|
||||||
|
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Attachment<span v-if="allAttachments(message).length > 1">s</span>
|
||||||
|
({{ allAttachments(message).length }})
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li v-for="part in allAttachments(message)">
|
||||||
|
<a :href="'api/'+message.ID+'/part/'+part.PartID" type="button"
|
||||||
|
class="dropdown-item" target="_blank">
|
||||||
|
<i class="bi bi-file-arrow-down-fill"></i>
|
||||||
|
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
||||||
|
<small class="text-muted ms-2">{{ getFileSize(part.Size) }}</small>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav nav-tabs my-3" id="nav-tab" role="tablist">
|
<div class="nav nav-tabs my-3" id="nav-tab" role="tablist">
|
||||||
<button class="nav-link" id="nav-html-tab" data-bs-toggle="tab"
|
<button class="nav-link" id="nav-html-tab" data-bs-toggle="tab"
|
||||||
data-bs-target="#nav-html" type="button" role="tab" aria-controls="nav-html"
|
data-bs-target="#nav-html" type="button" role="tab" aria-controls="nav-html"
|
||||||
aria-selected="true" :disabled="message.HTML == ''" :class="message.HTML == '' ? 'disabled':''">HTML</button>
|
aria-selected="true" v-if="message.HTML">HTML</button>
|
||||||
|
<button class="nav-link" id="nav-html-source-tab" data-bs-toggle="tab"
|
||||||
|
data-bs-target="#nav-html-source" type="button" role="tab" aria-controls="nav-html-source"
|
||||||
|
aria-selected="false" v-if="message.HTMLSource">HTML Source</button>
|
||||||
<button class="nav-link" id="nav-plain-text-tab" data-bs-toggle="tab"
|
<button class="nav-link" id="nav-plain-text-tab" data-bs-toggle="tab"
|
||||||
data-bs-target="#nav-plain-text" type="button" role="tab" aria-controls="nav-plain-text"
|
data-bs-target="#nav-plain-text" type="button" role="tab" aria-controls="nav-plain-text"
|
||||||
aria-selected="false" :class="message.HTML == '' ? 'show':''">Plain<span class="d-none d-md-inline"> text</span></button>
|
aria-selected="false" :class="message.HTML == '' ? 'show':''">Text</button>
|
||||||
<button class="nav-link" id="nav-source-tab" data-bs-toggle="tab"
|
<button class="nav-link" id="nav-raw-tab" data-bs-toggle="tab"
|
||||||
data-bs-target="#nav-source" type="button" role="tab" aria-controls="nav-source"
|
data-bs-target="#nav-raw" type="button" role="tab" aria-controls="nav-raw"
|
||||||
aria-selected="false">Source</button>
|
aria-selected="false">Raw</button>
|
||||||
<button class="nav-link" id="nav-mime-tab" data-bs-toggle="tab" data-bs-target="#nav-mime"
|
|
||||||
type="button" role="tab" aria-controls="nav-mime" aria-selected="false"
|
|
||||||
:disabled="!allAttachments(message)" :class="!allAttachments(message) ? 'disabled':''"
|
|
||||||
>Attachments <span v-if="allAttachments(message)">({{allAttachments(message).length}})</span></button>
|
|
||||||
<div class="d-none d-lg-block ms-auto small mt-3 me-2 text-muted">
|
|
||||||
<small>{{ messageDate(message.Date) }}</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="tab-content mb-5" id="nav-tabContent">
|
<div class="tab-content mb-5" id="nav-tabContent">
|
||||||
@ -169,26 +215,19 @@ export default {
|
|||||||
seamless frameborder="0" style="width: 100%; height: 100%;">
|
seamless frameborder="0" style="width: 100%; height: 100%;">
|
||||||
</iframe>
|
</iframe>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="nav-html-source" role="tabpanel"
|
||||||
|
aria-labelledby="nav-html-source-tab" tabindex="0" v-if="message.HTMLSource">
|
||||||
|
<pre><code class="language-html">{{ message.HTMLSource }}</code></pre>
|
||||||
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-plain-text" role="tabpanel"
|
<div class="tab-pane fade" id="nav-plain-text" role="tabpanel"
|
||||||
aria-labelledby="nav-plain-text-tab" tabindex="0" :class="message.HTML == '' ? 'show':''">
|
aria-labelledby="nav-plain-text-tab" tabindex="0" :class="message.HTML == '' ? 'show':''">
|
||||||
{{ message.Text }}
|
{{ message.Text }}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-source" role="tabpanel" aria-labelledby="nav-source-tab"
|
<div class="tab-pane fade" id="nav-raw" role="tabpanel" aria-labelledby="nav-raw-tab"
|
||||||
tabindex="0">
|
tabindex="0">
|
||||||
<iframe v-if="srcURI" :src="srcURI" v-on:load="resizeIframe"
|
<iframe v-if="srcURI" :src="srcURI" v-on:load="resizeIframe"
|
||||||
seamless frameborder="0" style="width: 100%; height: 300px;" id="message-src"></iframe>
|
seamless frameborder="0" style="width: 100%; height: 300px;" id="message-src"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-mime" role="tabpanel" aria-labelledby="nav-mime-tab"
|
|
||||||
tabindex="0">
|
|
||||||
<div v-if="allAttachments(message)" v-for="part in allAttachments(message)" class="mime-part mb-2">
|
|
||||||
<a :href="'api/'+message.ID+'/part/'+part.PartID" type="button"
|
|
||||||
class="btn btn-outline-secondary btn-sm me-2" target="_blank">
|
|
||||||
<i class="bi bi-file-arrow-down-fill"></i>
|
|
||||||
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
|
||||||
</a>
|
|
||||||
<small class="text-muted">{{ getFileSize(part.Size) }}</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -380,6 +380,7 @@ func GetMessage(id string) (*data.Message, error) {
|
|||||||
Subject: env.GetHeader("Subject"),
|
Subject: env.GetHeader("Subject"),
|
||||||
Size: len(raw),
|
Size: len(raw),
|
||||||
Text: env.Text,
|
Text: env.Text,
|
||||||
|
HTMLSource: env.HTML,
|
||||||
}
|
}
|
||||||
|
|
||||||
html := env.HTML
|
html := env.HTML
|
||||||
|
Loading…
x
Reference in New Issue
Block a user