mirror of
https://github.com/axllent/mailpit.git
synced 2025-07-15 01:25:10 +02:00
Feature: Convert links into clickable hyperlinks in plain text message content
@see 125
This commit is contained in:
@ -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, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
Reference in New Issue
Block a user