1
0
mirror of https://github.com/axllent/mailpit.git synced 2025-01-22 03:39:59 +02:00

Merge branch 'release/v1.6.20'

This commit is contained in:
Ralph Slooten 2023-06-15 17:34:15 +12:00
commit 3431f18a3f
3 changed files with 82 additions and 61 deletions

View File

@ -2,6 +2,15 @@
Notable changes to Mailpit will be documented in this file. Notable changes to Mailpit will be documented in this file.
## [v1.6.20]
### Feature
- Convert links into clickable hyperlinks in plain text message content
### Libs
- Update node modules
## [v1.6.19] ## [v1.6.19]
### Fix ### Fix

67
package-lock.json generated
View File

@ -36,9 +36,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.22.4", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
"integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
}, },
@ -47,9 +47,9 @@
} }
}, },
"node_modules/@babel/runtime-corejs3": { "node_modules/@babel/runtime-corejs3": {
"version": "7.22.3", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.22.3.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.22.5.tgz",
"integrity": "sha512-6bdmknScYKmt8I9VjsJuKKGr+TwUb555FTf6tT1P/ANlCjTHCiYLhiQ4X/O7J731w5NOqu8c1aYHEVuOwPz7jA==", "integrity": "sha512-TNPDN6aBFaUox2Lu+H/Y1dKKQgr4ucz/FGyCz67RVYLsBpVpUFf1dDngzg+Od8aqbrqwyztkaZjtWCZEUOT8zA==",
"dependencies": { "dependencies": {
"core-js-pure": "^3.30.2", "core-js-pure": "^3.30.2",
"regenerator-runtime": "^0.13.11" "regenerator-runtime": "^0.13.11"
@ -421,9 +421,9 @@
"integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==" "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ=="
}, },
"node_modules/@lit/reactive-element": { "node_modules/@lit/reactive-element": {
"version": "1.6.1", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.2.tgz",
"integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==", "integrity": "sha512-rDfl+QnCYjuIGf5xI2sVJWdYIi56CTCwWa+nidKYX6oIuBYwUbT/vX4qbUDlHiZKJ/3FRNQ/tWJui44p6/stSA==",
"dependencies": { "dependencies": {
"@lit-labs/ssr-dom-shim": "^1.0.0" "@lit-labs/ssr-dom-shim": "^1.0.0"
} }
@ -1063,9 +1063,9 @@
] ]
}, },
"node_modules/bootstrap5-tags": { "node_modules/bootstrap5-tags": {
"version": "1.5.22", "version": "1.5.24",
"resolved": "https://registry.npmjs.org/bootstrap5-tags/-/bootstrap5-tags-1.5.22.tgz", "resolved": "https://registry.npmjs.org/bootstrap5-tags/-/bootstrap5-tags-1.5.24.tgz",
"integrity": "sha512-wRpPtBBql6FvXKdSKUgFBkmmkGgsZJCzZ0bC9DvfHKylnQ4gS2pHpXB2UkIEWa/c+vENbBIyUDMSAvcZ4ujyQg==" "integrity": "sha512-+mH9WfNnz81CFoyui8G166YLe0ASEiSPb17DMdejCVdIOu7YGuSmYFfCAz/oqdD09vKege9yU6/YyXPOlGURGQ=="
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "2.0.1", "version": "2.0.1",
@ -1201,9 +1201,9 @@
} }
}, },
"node_modules/core-js-pure": { "node_modules/core-js-pure": {
"version": "3.30.2", "version": "3.31.0",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.2.tgz", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.31.0.tgz",
"integrity": "sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg==", "integrity": "sha512-/AnE9Y4OsJZicCzIe97JP5XoPKQJfTuEG43aEVLFJGOJpyqELod+pE6LEl63DfG1Mp8wX97LDaDpy1GmLEUxlg==",
"hasInstallScript": true, "hasInstallScript": true,
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -1683,9 +1683,9 @@
} }
}, },
"node_modules/lit": { "node_modules/lit": {
"version": "2.7.4", "version": "2.7.5",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.7.4.tgz", "resolved": "https://registry.npmjs.org/lit/-/lit-2.7.5.tgz",
"integrity": "sha512-cgD7xrZoYr21mbrkZIuIrj98YTMw/snJPg52deWVV4A8icLyNHI3bF70xsJeAgwTuiq5Kkd+ZR8gybSJDCPB7g==", "integrity": "sha512-i/cH7Ye6nBDUASMnfwcictBnsTN91+aBjXoTHF2xARghXScKxpD4F4WYI+VLXg9lqbMinDfvoI7VnZXjyHgdfQ==",
"dependencies": { "dependencies": {
"@lit/reactive-element": "^1.6.0", "@lit/reactive-element": "^1.6.0",
"lit-element": "^3.3.0", "lit-element": "^3.3.0",
@ -2062,9 +2062,9 @@
} }
}, },
"node_modules/punycode": { "node_modules/punycode": {
"version": "1.3.2", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
}, },
"node_modules/qs": { "node_modules/qs": {
"version": "6.11.2", "version": "6.11.2",
@ -2080,15 +2080,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/ramda": { "node_modules/ramda": {
"version": "0.29.0", "version": "0.29.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz",
@ -2209,9 +2200,9 @@
"optional": true "optional": true
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.62.1", "version": "1.63.4",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.4.tgz",
"integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==", "integrity": "sha512-Sx/+weUmK+oiIlI+9sdD0wZHsqpbgQg8wSwSnGBjwb5GwqFhYNwwnI+UWZtLjKvKyFlKkatRK235qQ3mokyPoQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"chokidar": ">=3.0.0 <4.0.0", "chokidar": ">=3.0.0 <4.0.0",
@ -2526,12 +2517,12 @@
"integrity": "sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ==" "integrity": "sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ=="
}, },
"node_modules/url": { "node_modules/url": {
"version": "0.11.0", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "resolved": "https://registry.npmjs.org/url/-/url-0.11.1.tgz",
"integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", "integrity": "sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==",
"dependencies": { "dependencies": {
"punycode": "1.3.2", "punycode": "^1.4.1",
"querystring": "0.2.0" "qs": "^6.11.0"
} }
}, },
"node_modules/util-deprecate": { "node_modules/util-deprecate": {

View File

@ -163,6 +163,27 @@ export default {
self.scrollInPlace = true; self.scrollInPlace = true;
self.$emit('loadMessages'); self.$emit('loadMessages');
}); });
},
// Convert plain text to HTML including anchor links
textToHTML: function (s) {
// escape to HTML
let html = s
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;")
// full links with http(s)
let re = /(\b(https?|ftp):\/\/[\-\w@:%_\+.~#?,&\/\/=;]+)\b/gim
html = html.replace(re, '<a href="$&" target="_blank" rel="noopener">$&</a>')
// plain www links without https?:// prefix
let re2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim
html = html.replace(re2, '$1<a href="http://$2" target="_blank" rel="noopener">$2</a>')
return html
} }
} }
} }
@ -272,28 +293,28 @@ export default {
role="tab" aria-controls="nav-html" aria-selected="true" v-if="message.HTML" role="tab" aria-controls="nav-html" aria-selected="true" v-if="message.HTML"
v-on:click="showMobileBtns = true; resizeIframes()">HTML</button> v-on:click="showMobileBtns = true; resizeIframes()">HTML</button>
<button class="nav-link" id="nav-html-source-tab" data-bs-toggle="tab" data-bs-target="#nav-html-source" <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.HTML " type="button" role="tab" aria-controls="nav-html-source" aria-selected="false" v-if="message.HTML"
v-on:click=" showMobileBtns = false "> v-on:click=" showMobileBtns = false">
HTML <span class="d-sm-none">Src</span><span class="d-none d-sm-inline">Source</span> HTML <span class="d-sm-none">Src</span><span class="d-none d-sm-inline">Source</span>
</button> </button>
<button class="nav-link" id="nav-plain-text-tab" data-bs-toggle="tab" data-bs-target="#nav-plain-text" <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" aria-selected="false" type="button" role="tab" aria-controls="nav-plain-text" aria-selected="false"
:class=" message.HTML == '' ? 'show' : '' " v-on:click=" showMobileBtns = false ">Text</button> :class="message.HTML == '' ? 'show' : ''" v-on:click=" showMobileBtns = false">Text</button>
<button class="nav-link" id="nav-headers-tab" data-bs-toggle="tab" data-bs-target="#nav-headers" <button class="nav-link" id="nav-headers-tab" data-bs-toggle="tab" data-bs-target="#nav-headers"
type="button" role="tab" aria-controls="nav-headers" aria-selected="false" type="button" role="tab" aria-controls="nav-headers" aria-selected="false"
v-on:click=" showMobileBtns = false "> v-on:click=" showMobileBtns = false">
<span class="d-sm-none">Hdrs</span><span class="d-none d-sm-inline">Headers</span> <span class="d-sm-none">Hdrs</span><span class="d-none d-sm-inline">Headers</span>
</button> </button>
<button class="nav-link" id="nav-raw-tab" data-bs-toggle="tab" data-bs-target="#nav-raw" type="button" <button class="nav-link" id="nav-raw-tab" data-bs-toggle="tab" data-bs-target="#nav-raw" type="button"
role="tab" aria-controls="nav-raw" aria-selected="false" role="tab" aria-controls="nav-raw" aria-selected="false"
v-on:click=" showMobileBtns = false ">Raw</button> v-on:click=" showMobileBtns = false">Raw</button>
<div class="d-none d-lg-block ms-auto me-2" v-if=" showMobileBtns "> <div class="d-none d-lg-block ms-auto me-2" v-if="showMobileBtns">
<template v-for=" vals, key in responsiveSizes "> <template v-for=" vals, key in responsiveSizes ">
<button class="btn" :class=" scaleHTMLPreview == key ? 'btn-outline-primary' : '' " <button class="btn" :class="scaleHTMLPreview == key ? 'btn-outline-primary' : ''"
:disabled=" scaleHTMLPreview == key " :title=" 'Switch to ' + key + ' view' " :disabled="scaleHTMLPreview == key" :title="'Switch to ' + key + ' view'"
v-on:click=" scaleHTMLPreview = key "> v-on:click=" scaleHTMLPreview = key">
<i class="bi" :class=" 'bi-' + key "></i> <i class="bi" :class="'bi-' + key"></i>
</button> </button>
</template> </template>
</div> </div>
@ -301,31 +322,31 @@ export default {
</nav> </nav>
<div class="tab-content mb-5" id="nav-tabContent"> <div class="tab-content mb-5" id="nav-tabContent">
<div v-if=" message.HTML != '' " class="tab-pane fade show" id="nav-html" role="tabpanel" <div v-if="message.HTML != ''" class="tab-pane fade show" id="nav-html" role="tabpanel"
aria-labelledby="nav-html-tab" tabindex="0"> aria-labelledby="nav-html-tab" tabindex="0">
<div id="responsive-view" :class=" scaleHTMLPreview " :style=" responsiveSizes[scaleHTMLPreview] "> <div id="responsive-view" :class="scaleHTMLPreview" :style="responsiveSizes[scaleHTMLPreview]">
<iframe target-blank="" class="tab-pane d-block" id="preview-html" :srcdoc=" message.HTML " <iframe target-blank="" class="tab-pane d-block" id="preview-html" :srcdoc="message.HTML"
v-on:load=" resizeIframe " seamless frameborder="0" style="width: 100%; height: 100%;"> v-on:load="resizeIframe" seamless frameborder="0" style="width: 100%; height: 100%;">
</iframe> </iframe>
</div> </div>
<Attachments v-if=" allAttachments(message).length " :message=" message " <Attachments v-if="allAttachments(message).length" :message="message"
:attachments=" allAttachments(message) "></Attachments> :attachments="allAttachments(message)"></Attachments>
</div> </div>
<div class="tab-pane fade" id="nav-html-source" role="tabpanel" aria-labelledby="nav-html-source-tab" <div class="tab-pane fade" id="nav-html-source" role="tabpanel" aria-labelledby="nav-html-source-tab"
tabindex="0" v-if=" message.HTML "> tabindex="0" v-if="message.HTML">
<pre><code class="language-html">{{ message.HTML }}</code></pre> <pre><code class="language-html">{{ message.HTML }}</code></pre>
</div> </div>
<div class="tab-pane fade" id="nav-plain-text" role="tabpanel" aria-labelledby="nav-plain-text-tab" tabindex="0" <div class="tab-pane fade" id="nav-plain-text" role="tabpanel" aria-labelledby="nav-plain-text-tab" tabindex="0"
:class=" message.HTML == '' ? 'show' : '' "> :class="message.HTML == '' ? 'show' : ''">
<div class="text-view">{{ message.Text }}</div> <div class="text-view" v-html="textToHTML(message.Text)"></div>
<Attachments v-if=" allAttachments(message).length " :message=" message " <Attachments v-if="allAttachments(message).length" :message="message"
:attachments=" allAttachments(message) "></Attachments> :attachments="allAttachments(message)"></Attachments>
</div> </div>
<div class="tab-pane fade" id="nav-headers" role="tabpanel" aria-labelledby="nav-headers-tab" tabindex="0"> <div class="tab-pane fade" id="nav-headers" role="tabpanel" aria-labelledby="nav-headers-tab" tabindex="0">
<Headers v-if=" loadHeaders " :message=" message "></Headers> <Headers v-if="loadHeaders" :message="message"></Headers>
</div> </div>
<div class="tab-pane fade" id="nav-raw" role="tabpanel" aria-labelledby="nav-raw-tab" tabindex="0"> <div class="tab-pane fade" id="nav-raw" role="tabpanel" aria-labelledby="nav-raw-tab" tabindex="0">
<iframe v-if=" srcURI " :src=" srcURI " v-on:load=" resizeIframe " seamless frameborder="0" <iframe v-if="srcURI" :src="srcURI" v-on:load="resizeIframe" seamless frameborder="0"
style="width: 100%; height: 300px;" id="message-src"></iframe> style="width: 100%; height: 300px;" id="message-src"></iframe>
</div> </div>
</div> </div>