mirror of
https://github.com/axllent/mailpit.git
synced 2025-05-15 22:16:44 +02:00
Chore: Refactor JavaScript, use arrow functions instead of "self" aliasing
This commit is contained in:
parent
5e5b855a3d
commit
33e367d706
@ -26,15 +26,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadInfo: function () {
|
loadInfo() {
|
||||||
let self = this
|
this.get(this.resolve('/api/v1/info'), false, (response) => {
|
||||||
self.get(self.resolve('/api/v1/info'), false, function (response) {
|
|
||||||
mailbox.appInfo = response.data
|
mailbox.appInfo = response.data
|
||||||
self.modal('AppInfoModal').show()
|
this.modal('AppInfoModal').show()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
requestNotifications: function () {
|
requestNotifications() {
|
||||||
// check if the browser supports notifications
|
// check if the browser supports notifications
|
||||||
if (!("Notification" in window)) {
|
if (!("Notification" in window)) {
|
||||||
alert("This browser does not support desktop notifications")
|
alert("This browser does not support desktop notifications")
|
||||||
@ -42,7 +41,6 @@ export default {
|
|||||||
|
|
||||||
// we need to ask the user for permission
|
// we need to ask the user for permission
|
||||||
else if (Notification.permission !== "denied") {
|
else if (Notification.permission !== "denied") {
|
||||||
let self = this
|
|
||||||
Notification.requestPermission().then(function (permission) {
|
Notification.requestPermission().then(function (permission) {
|
||||||
if (permission === "granted") {
|
if (permission === "granted") {
|
||||||
mailbox.notificationsEnabled = true
|
mailbox.notificationsEnabled = true
|
||||||
@ -180,7 +178,9 @@ export default {
|
|||||||
<td>
|
<td>
|
||||||
{{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPAccepted) }}
|
{{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPAccepted) }}
|
||||||
<small class="text-secondary">
|
<small class="text-secondary">
|
||||||
({{ getFileSize(mailbox.appInfo.RuntimeStats.SMTPAcceptedSize) }})
|
({{
|
||||||
|
getFileSize(mailbox.appInfo.RuntimeStats.SMTPAcceptedSize)
|
||||||
|
}})
|
||||||
</small>
|
</small>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -245,6 +245,6 @@ export default {
|
|||||||
|
|
||||||
<Settings />
|
<Settings />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<AjaxLoader :loading="loading" />
|
<AjaxLoader :loading="loading" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -39,10 +39,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.iconProcessing = true
|
this.iconProcessing = true
|
||||||
let self = this
|
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
self.icoUpdate()
|
this.icoUpdate()
|
||||||
}, this.iconTimeout)
|
}, this.iconTimeout)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -21,29 +21,25 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
let relativeTime = require('dayjs/plugin/relativeTime')
|
const relativeTime = require('dayjs/plugin/relativeTime')
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
this.refreshUI()
|
this.refreshUI()
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
refreshUI: function () {
|
refreshUI() {
|
||||||
let self = this
|
window.setTimeout(() => {
|
||||||
window.setTimeout(
|
this.$forceUpdate()
|
||||||
() => {
|
this.refreshUI()
|
||||||
self.$forceUpdate()
|
}, 30000)
|
||||||
self.refreshUI()
|
|
||||||
},
|
|
||||||
30000
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getRelativeCreated: function (message) {
|
getRelativeCreated(message) {
|
||||||
let d = new Date(message.Created)
|
const d = new Date(message.Created)
|
||||||
return dayjs(d).fromNow()
|
return dayjs(d).fromNow()
|
||||||
},
|
},
|
||||||
|
|
||||||
getPrimaryEmailTo: function (message) {
|
getPrimaryEmailTo(message) {
|
||||||
for (let i in message.To) {
|
for (let i in message.To) {
|
||||||
return message.To[i].Address
|
return message.To[i].Address
|
||||||
}
|
}
|
||||||
@ -51,11 +47,11 @@ export default {
|
|||||||
return '[ Undisclosed recipients ]'
|
return '[ Undisclosed recipients ]'
|
||||||
},
|
},
|
||||||
|
|
||||||
isSelected: function (id) {
|
isSelected(id) {
|
||||||
return mailbox.selected.indexOf(id) != -1
|
return mailbox.selected.indexOf(id) != -1
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSelected: function (e, id) {
|
toggleSelected(e, id) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (this.isSelected(id)) {
|
if (this.isSelected(id)) {
|
||||||
@ -67,7 +63,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
selectRange: function (e, id) {
|
selectRange(e, id) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
let selecting = false
|
let selecting = false
|
||||||
@ -102,7 +98,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toTagUrl: function (t) {
|
toTagUrl(t) {
|
||||||
if (t.match(/ /)) {
|
if (t.match(/ /)) {
|
||||||
t = `"${t}"`
|
t = `"${t}"`
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
reloadInbox: function () {
|
reloadInbox() {
|
||||||
const paginationParams = this.getPaginationParams()
|
const paginationParams = this.getPaginationParams()
|
||||||
const reload = paginationParams?.start ? false : true
|
const reload = paginationParams?.start ? false : true
|
||||||
|
|
||||||
@ -41,24 +41,22 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMessages: function () {
|
loadMessages() {
|
||||||
this.hideNav() // hide mobile menu
|
this.hideNav() // hide mobile menu
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
},
|
},
|
||||||
|
|
||||||
markAllRead: function () {
|
markAllRead() {
|
||||||
let self = this
|
this.put(this.resolve(`/api/v1/messages`), { 'read': true }, (response) => {
|
||||||
self.put(self.resolve(`/api/v1/messages`), { 'read': true }, function (response) {
|
|
||||||
window.scrollInPlace = true
|
window.scrollInPlace = true
|
||||||
self.loadMessages()
|
this.loadMessages()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteAllMessages: function () {
|
deleteAllMessages() {
|
||||||
let self = this
|
this.delete(this.resolve(`/api/v1/messages`), false, (response) => {
|
||||||
self.delete(self.resolve(`/api/v1/messages`), false, function (response) {
|
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
self.loadMessages()
|
this.loadMessages()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,22 +30,20 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadMessages: function () {
|
loadMessages() {
|
||||||
this.hideNav() // hide mobile menu
|
this.hideNav() // hide mobile menu
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteAllMessages: function () {
|
deleteAllMessages() {
|
||||||
let s = this.getSearch()
|
const s = this.getSearch()
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this
|
const uri = this.resolve(`/api/v1/search`) + '?query=' + encodeURIComponent(s)
|
||||||
|
this.delete(uri, false, (response) => {
|
||||||
let uri = this.resolve(`/api/v1/search`) + '?query=' + encodeURIComponent(s)
|
this.$router.push('/')
|
||||||
this.delete(uri, false, function (response) {
|
|
||||||
self.$router.push('/')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,54 +19,52 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadMessages: function () {
|
loadMessages() {
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
},
|
},
|
||||||
|
|
||||||
// mark selected messages as read
|
// mark selected messages as read
|
||||||
markSelectedRead: function () {
|
markSelectedRead() {
|
||||||
let self = this
|
|
||||||
if (!mailbox.selected.length) {
|
if (!mailbox.selected.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.put(self.resolve(`/api/v1/messages`), { 'Read': true, 'IDs': mailbox.selected }, function (response) {
|
this.put(this.resolve(`/api/v1/messages`), { 'Read': true, 'IDs': mailbox.selected }, (response) => {
|
||||||
window.scrollInPlace = true
|
window.scrollInPlace = true
|
||||||
self.loadMessages()
|
this.loadMessages()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
isSelected: function (id) {
|
isSelected(id) {
|
||||||
return mailbox.selected.indexOf(id) != -1
|
return mailbox.selected.indexOf(id) != -1
|
||||||
},
|
},
|
||||||
|
|
||||||
// mark selected messages as unread
|
// mark selected messages as unread
|
||||||
markSelectedUnread: function () {
|
markSelectedUnread() {
|
||||||
let self = this
|
|
||||||
if (!mailbox.selected.length) {
|
if (!mailbox.selected.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.put(self.resolve(`/api/v1/messages`), { 'Read': false, 'IDs': mailbox.selected }, function (response) {
|
this.put(this.resolve(`/api/v1/messages`), { 'Read': false, 'IDs': mailbox.selected }, (response) => {
|
||||||
window.scrollInPlace = true
|
window.scrollInPlace = true
|
||||||
self.loadMessages()
|
this.loadMessages()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// universal handler to delete current or selected messages
|
// universal handler to delete current or selected messages
|
||||||
deleteMessages: function () {
|
deleteMessages() {
|
||||||
let ids = []
|
let ids = []
|
||||||
let self = this
|
|
||||||
ids = JSON.parse(JSON.stringify(mailbox.selected))
|
ids = JSON.parse(JSON.stringify(mailbox.selected))
|
||||||
if (!ids.length) {
|
if (!ids.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.delete(self.resolve(`/api/v1/messages`), { 'IDs': ids }, function (response) {
|
|
||||||
|
this.delete(this.resolve(`/api/v1/messages`), { 'IDs': ids }, (response) => {
|
||||||
window.scrollInPlace = true
|
window.scrollInPlace = true
|
||||||
self.loadMessages()
|
this.loadMessages()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// test if any selected emails are unread
|
// test if any selected emails are unread
|
||||||
selectedHasUnread: function () {
|
selectedHasUnread() {
|
||||||
if (!mailbox.selected.length) {
|
if (!mailbox.selected.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -79,7 +77,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// test of any selected emails are read
|
// test of any selected emails are read
|
||||||
selectedHasRead: function () {
|
selectedHasRead() {
|
||||||
if (!mailbox.selected.length) {
|
if (!mailbox.selected.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// test whether a tag is currently being searched for (in the URL)
|
// test whether a tag is currently being searched for (in the URL)
|
||||||
inSearch: function (tag) {
|
inSearch(tag) {
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
const query = urlParams.get('q')
|
const query = urlParams.get('q')
|
||||||
if (!query) {
|
if (!query) {
|
||||||
@ -29,7 +29,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// toggle a tag search in the search URL, add or remove it accordingly
|
// toggle a tag search in the search URL, add or remove it accordingly
|
||||||
toggleTag: function (e, tag) {
|
toggleTag(e, tag) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
@ -43,8 +43,7 @@ export default {
|
|||||||
// websocket connect
|
// websocket connect
|
||||||
connect() {
|
connect() {
|
||||||
const ws = new WebSocket(this.socketURI)
|
const ws = new WebSocket(this.socketURI)
|
||||||
let self = this
|
ws.onmessage = (e) => {
|
||||||
ws.onmessage = function (e) {
|
|
||||||
let response
|
let response
|
||||||
try {
|
try {
|
||||||
response = JSON.parse(e.data)
|
response = JSON.parse(e.data)
|
||||||
@ -65,7 +64,7 @@ export default {
|
|||||||
// update pagination offset
|
// update pagination offset
|
||||||
pagination.start++
|
pagination.start++
|
||||||
// prevent "Too many calls to Location or History APIs within a short timeframe"
|
// prevent "Too many calls to Location or History APIs within a short timeframe"
|
||||||
self.delayedPaginationUpdate()
|
this.delayedPaginationUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,13 +78,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send notifications
|
// send notifications
|
||||||
if (!self.pauseNotifications) {
|
if (!this.pauseNotifications) {
|
||||||
self.pauseNotifications = true
|
this.pauseNotifications = true
|
||||||
let from = response.Data.From != null ? response.Data.From.Address : '[unknown]'
|
let from = response.Data.From != null ? response.Data.From.Address : '[unknown]'
|
||||||
self.browserNotify("New mail from: " + from, response.Data.Subject)
|
this.browserNotify("New mail from: " + from, response.Data.Subject)
|
||||||
self.setMessageToast(response.Data)
|
this.setMessageToast(response.Data)
|
||||||
// delay notifications by 2s
|
// delay notifications by 2s
|
||||||
window.setTimeout(() => { self.pauseNotifications = false }, 2000)
|
window.setTimeout(() => { this.pauseNotifications = false }, 2000)
|
||||||
}
|
}
|
||||||
} else if (response.Type == "prune") {
|
} else if (response.Type == "prune") {
|
||||||
// messages have been deleted, reload messages to adjust
|
// messages have been deleted, reload messages to adjust
|
||||||
@ -98,24 +97,24 @@ export default {
|
|||||||
mailbox.unread = response.Data.Unread
|
mailbox.unread = response.Data.Unread
|
||||||
|
|
||||||
// detect version updated, refresh is needed
|
// detect version updated, refresh is needed
|
||||||
if (self.version != response.Data.Version) {
|
if (this.version != response.Data.Version) {
|
||||||
location.reload()
|
location.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onopen = function () {
|
ws.onopen = () => {
|
||||||
mailbox.connected = true
|
mailbox.connected = true
|
||||||
self.socketLastConnection = Date.now()
|
this.socketLastConnection = Date.now()
|
||||||
if (self.reconnectRefresh) {
|
if (this.reconnectRefresh) {
|
||||||
self.reconnectRefresh = false
|
this.reconnectRefresh = false
|
||||||
mailbox.refresh = true // trigger refresh
|
mailbox.refresh = true // trigger refresh
|
||||||
window.setTimeout(() => { mailbox.refresh = false }, 500)
|
window.setTimeout(() => { mailbox.refresh = false }, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onclose = function (e) {
|
ws.onclose = (e) => {
|
||||||
if (self.socketLastConnection == 0) {
|
if (this.socketLastConnection == 0) {
|
||||||
// connection failed immediately after connecting to Mailpit implies proxy websockets aren't configured
|
// connection failed immediately after connecting to Mailpit implies proxy websockets aren't configured
|
||||||
console.log('Unable to connect to websocket, disabling websocket support')
|
console.log('Unable to connect to websocket, disabling websocket support')
|
||||||
return
|
return
|
||||||
@ -123,27 +122,27 @@ export default {
|
|||||||
|
|
||||||
if (mailbox.connected) {
|
if (mailbox.connected) {
|
||||||
// count disconnections
|
// count disconnections
|
||||||
self.socketBreaks++
|
this.socketBreaks++
|
||||||
}
|
}
|
||||||
|
|
||||||
// set disconnected state
|
// set disconnected state
|
||||||
mailbox.connected = false
|
mailbox.connected = false
|
||||||
|
|
||||||
if (self.socketBreaks > 3) {
|
if (this.socketBreaks > 3) {
|
||||||
// give up after > 3 successful socket connections & disconnections within a 15 second window,
|
// give up after > 3 successful socket connections & disconnections within a 15 second window,
|
||||||
// something is not working right on their end, see issue #319
|
// something is not working right on their end, see issue #319
|
||||||
console.log('Unstable websocket connection, disabling websocket support')
|
console.log('Unstable websocket connection, disabling websocket support')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (Date.now() - self.socketLastConnection > 5000) {
|
if (Date.now() - this.socketLastConnection > 5000) {
|
||||||
// only refresh mailbox if the last successful connection was broken for > 5 seconds
|
// only refresh mailbox if the last successful connection was broken for > 5 seconds
|
||||||
self.reconnectRefresh = true
|
this.reconnectRefresh = true
|
||||||
} else {
|
} else {
|
||||||
self.reconnectRefresh = false
|
this.reconnectRefresh = false
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
self.connect() // reconnect
|
this.connect() // reconnect
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,11 +213,10 @@ export default {
|
|||||||
|
|
||||||
this.toastMessage = m
|
this.toastMessage = m
|
||||||
|
|
||||||
let self = this
|
|
||||||
const el = document.getElementById('messageToast')
|
const el = document.getElementById('messageToast')
|
||||||
if (el) {
|
if (el) {
|
||||||
el.addEventListener('hidden.bs.toast', () => {
|
el.addEventListener('hidden.bs.toast', () => {
|
||||||
self.toastMessage = false
|
this.toastMessage = false
|
||||||
})
|
})
|
||||||
|
|
||||||
Toast.getOrCreateInstance(el).show()
|
Toast.getOrCreateInstance(el).show()
|
||||||
|
@ -20,16 +20,16 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
canPrev: function () {
|
canPrev() {
|
||||||
return pagination.start > 0
|
return pagination.start > 0
|
||||||
},
|
},
|
||||||
|
|
||||||
canNext: function () {
|
canNext() {
|
||||||
return this.total > (pagination.start + mailbox.messages.length)
|
return this.total > (pagination.start + mailbox.messages.length)
|
||||||
},
|
},
|
||||||
|
|
||||||
// returns the number of next X messages
|
// returns the number of next X messages
|
||||||
nextMessages: function () {
|
nextMessages() {
|
||||||
let t = pagination.start + parseInt(pagination.limit, 10)
|
let t = pagination.start + parseInt(pagination.limit, 10)
|
||||||
if (t > this.total) {
|
if (t > this.total) {
|
||||||
t = this.total
|
t = this.total
|
||||||
@ -40,17 +40,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
changeLimit: function () {
|
changeLimit() {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
this.updateQueryParams()
|
this.updateQueryParams()
|
||||||
},
|
},
|
||||||
|
|
||||||
viewNext: function () {
|
viewNext() {
|
||||||
pagination.start = parseInt(pagination.start, 10) + parseInt(pagination.limit, 10)
|
pagination.start = parseInt(pagination.start, 10) + parseInt(pagination.limit, 10)
|
||||||
this.updateQueryParams()
|
this.updateQueryParams()
|
||||||
},
|
},
|
||||||
|
|
||||||
viewPrev: function () {
|
viewPrev() {
|
||||||
let s = pagination.start - pagination.limit
|
let s = pagination.start - pagination.limit
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
s = 0
|
s = 0
|
||||||
@ -59,7 +59,7 @@ export default {
|
|||||||
this.updateQueryParams()
|
this.updateQueryParams()
|
||||||
},
|
},
|
||||||
|
|
||||||
updateQueryParams: function () {
|
updateQueryParams() {
|
||||||
const path = this.$route.path
|
const path = this.$route.path
|
||||||
const p = {
|
const p = {
|
||||||
...this.$route.query
|
...this.$route.query
|
||||||
|
@ -24,18 +24,18 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
searchFromURL: function () {
|
searchFromURL() {
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
this.search = urlParams.get('q') ? urlParams.get('q') : ''
|
this.search = urlParams.get('q') ? urlParams.get('q') : ''
|
||||||
},
|
},
|
||||||
|
|
||||||
doSearch: function (e) {
|
doSearch(e) {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
if (this.search == '') {
|
if (this.search == '') {
|
||||||
this.$router.push('/')
|
this.$router.push('/')
|
||||||
} else {
|
} else {
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
let curr = urlParams.get('q')
|
const curr = urlParams.get('q')
|
||||||
if (curr && curr == this.search) {
|
if (curr && curr == this.search) {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
@ -57,7 +57,7 @@ export default {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
},
|
},
|
||||||
|
|
||||||
resetSearch: function () {
|
resetSearch() {
|
||||||
this.search = ''
|
this.search = ''
|
||||||
this.$router.push('/')
|
this.$router.push('/')
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
theme: function (v) {
|
theme(v) {
|
||||||
if (v == 'auto') {
|
if (v == 'auto') {
|
||||||
localStorage.removeItem('theme')
|
localStorage.removeItem('theme')
|
||||||
} else {
|
} else {
|
||||||
@ -34,7 +34,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setTheme: function () {
|
setTheme() {
|
||||||
if (
|
if (
|
||||||
this.theme === 'auto' &&
|
this.theme === 'auto' &&
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
@ -18,7 +18,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
openAttachment: function (part, e) {
|
openAttachment(part, e) {
|
||||||
let filename = part.FileName
|
let filename = part.FileName
|
||||||
let contentType = part.ContentType
|
let contentType = part.ContentType
|
||||||
let href = this.resolve('/api/v1/message/' + this.message.ID + '/part/' + part.PartID)
|
let href = this.resolve('/api/v1/message/' + this.message.ID + '/part/' + part.PartID)
|
||||||
|
@ -41,9 +41,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
summary: function () {
|
summary() {
|
||||||
let self = this
|
|
||||||
|
|
||||||
if (!this.check) {
|
if (!this.check) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -65,8 +63,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// filter by enabled platforms
|
// filter by enabled platforms
|
||||||
let results = o.Results.filter(function (w) {
|
let results = o.Results.filter((w) => {
|
||||||
return self.platforms.indexOf(w.Platform) != -1
|
return this.platforms.indexOf(w.Platform) != -1
|
||||||
})
|
})
|
||||||
|
|
||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
@ -98,7 +96,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let maxPartial = 0, maxUnsupported = 0
|
let maxPartial = 0, maxUnsupported = 0
|
||||||
result.Warnings.forEach(function (w) {
|
result.Warnings.forEach((w) => {
|
||||||
let scoreWeight = 1
|
let scoreWeight = 1
|
||||||
if (w.Score.Found < result.Total.Nodes) {
|
if (w.Score.Found < result.Total.Nodes) {
|
||||||
// each error is weighted based on the number of occurrences vs: the total message nodes
|
// each error is weighted based on the number of occurrences vs: the total message nodes
|
||||||
@ -108,7 +106,7 @@ export default {
|
|||||||
// pseudo-classes & at-rules need to be weighted lower as we do not know how many times they
|
// pseudo-classes & at-rules need to be weighted lower as we do not know how many times they
|
||||||
// are actually used in the HTML, and including things like bootstrap styles completely throws
|
// are actually used in the HTML, and including things like bootstrap styles completely throws
|
||||||
// off the calculation as these dominate.
|
// off the calculation as these dominate.
|
||||||
if (self.isPseudoClassOrAtRule(w.Title)) {
|
if (this.isPseudoClassOrAtRule(w.Title)) {
|
||||||
scoreWeight = 0.05
|
scoreWeight = 0.05
|
||||||
w.PseudoClassOrAtRule = true
|
w.PseudoClassOrAtRule = true
|
||||||
}
|
}
|
||||||
@ -124,15 +122,15 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// sort warnings by final score
|
// sort warnings by final score
|
||||||
result.Warnings.sort(function (a, b) {
|
result.Warnings.sort((a, b) => {
|
||||||
let aWeight = a.Score.Found > result.Total.Nodes ? result.Total.Nodes : a.Score.Found / result.Total.Nodes
|
let aWeight = a.Score.Found > result.Total.Nodes ? result.Total.Nodes : a.Score.Found / result.Total.Nodes
|
||||||
let bWeight = b.Score.Found > result.Total.Nodes ? result.Total.Nodes : b.Score.Found / result.Total.Nodes
|
let bWeight = b.Score.Found > result.Total.Nodes ? result.Total.Nodes : b.Score.Found / result.Total.Nodes
|
||||||
|
|
||||||
if (self.isPseudoClassOrAtRule(a.Title)) {
|
if (this.isPseudoClassOrAtRule(a.Title)) {
|
||||||
aWeight = 0.05
|
aWeight = 0.05
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.isPseudoClassOrAtRule(b.Title)) {
|
if (this.isPseudoClassOrAtRule(b.Title)) {
|
||||||
bWeight = 0.05
|
bWeight = 0.05
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +146,7 @@ export default {
|
|||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
|
||||||
graphSections: function () {
|
graphSections() {
|
||||||
let s = Math.round(this.summary.Total.Supported)
|
let s = Math.round(this.summary.Total.Supported)
|
||||||
let p = Math.round(this.summary.Total.Partial)
|
let p = Math.round(this.summary.Total.Partial)
|
||||||
let u = 100 - s - p
|
let u = 100 - s - p
|
||||||
@ -172,7 +170,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// colors depend on both varying unsupported & partially unsupported percentages
|
// colors depend on both varying unsupported & partially unsupported percentages
|
||||||
scoreColor: function () {
|
scoreColor() {
|
||||||
if (this.summary.Total.Unsupported < 5 && this.summary.Total.Partial < 10) {
|
if (this.summary.Total.Unsupported < 5 && this.summary.Total.Partial < 10) {
|
||||||
this.$emit('setBadgeStyle', 'bg-success')
|
this.$emit('setBadgeStyle', 'bg-success')
|
||||||
return 'text-success'
|
return 'text-success'
|
||||||
@ -197,62 +195,51 @@ export default {
|
|||||||
platforms(v) {
|
platforms(v) {
|
||||||
localStorage.setItem('html-check-platforms', JSON.stringify(v))
|
localStorage.setItem('html-check-platforms', JSON.stringify(v))
|
||||||
},
|
},
|
||||||
// enabled(v) {
|
|
||||||
// if (!v) {
|
|
||||||
// localStorage.setItem('htmlCheckDisabled', true)
|
|
||||||
// this.$emit('setHtmlScore', false)
|
|
||||||
// } else {
|
|
||||||
// localStorage.removeItem('htmlCheckDisabled')
|
|
||||||
// this.doCheck()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
doCheck: function () {
|
doCheck() {
|
||||||
this.check = false
|
this.check = false
|
||||||
|
|
||||||
if (this.message.HTML == "") {
|
if (this.message.HTML == "") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this
|
|
||||||
|
|
||||||
// ignore any error, do not show loader
|
// ignore any error, do not show loader
|
||||||
axios.get(self.resolve('/api/v1/message/' + self.message.ID + '/html-check'), null)
|
axios.get(this.resolve('/api/v1/message/' + this.message.ID + '/html-check'), null)
|
||||||
.then(function (result) {
|
.then((result) => {
|
||||||
self.check = result.data
|
this.check = result.data
|
||||||
self.error = false
|
this.error = false
|
||||||
|
|
||||||
// set tooltips
|
// set tooltips
|
||||||
window.setTimeout(function () {
|
window.setTimeout(() => {
|
||||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||||
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
||||||
}, 500)
|
}, 500)
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
// handle error
|
// handle error
|
||||||
if (error.response && error.response.data) {
|
if (error.response && error.response.data) {
|
||||||
// The request was made and the server responded with a status code
|
// The request was made and the server responded with a status code
|
||||||
// that falls out of the range of 2xx
|
// that falls out of the range of 2xx
|
||||||
if (error.response.data.Error) {
|
if (error.response.data.Error) {
|
||||||
self.error = error.response.data.Error
|
this.error = error.response.data.Error
|
||||||
} else {
|
} else {
|
||||||
self.error = error.response.data
|
this.error = error.response.data
|
||||||
}
|
}
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
// The request was made but no response was received
|
// The request was made but no response was received
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
// http.ClientRequest in node.js
|
// http.ClientRequest in node.js
|
||||||
self.error = 'Error sending data to the server. Please try again.'
|
this.error = 'Error sending data to the server. Please try again.'
|
||||||
} else {
|
} else {
|
||||||
// Something happened in setting up the request that triggered an Error
|
// Something happened in setting up the request that triggered an Error
|
||||||
self.error = error.message
|
this.error = error.message
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
loadConfig: function () {
|
loadConfig() {
|
||||||
let platforms = localStorage.getItem('html-check-platforms')
|
let platforms = localStorage.getItem('html-check-platforms')
|
||||||
if (platforms) {
|
if (platforms) {
|
||||||
try {
|
try {
|
||||||
@ -268,7 +255,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// return a platform's families (email clients)
|
// return a platform's families (email clients)
|
||||||
families: function (k) {
|
families(k) {
|
||||||
if (this.check.Platforms[k]) {
|
if (this.check.Platforms[k]) {
|
||||||
return this.check.Platforms[k]
|
return this.check.Platforms[k]
|
||||||
}
|
}
|
||||||
@ -277,19 +264,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// return whether the test string is a pseudo class (:<test>) or at rule (@<test>)
|
// return whether the test string is a pseudo class (:<test>) or at rule (@<test>)
|
||||||
isPseudoClassOrAtRule: function (t) {
|
isPseudoClassOrAtRule(t) {
|
||||||
return t.match(/^(:|@)/)
|
return t.match(/^(:|@)/)
|
||||||
},
|
},
|
||||||
|
|
||||||
round: function (v) {
|
round(v) {
|
||||||
return Math.round(v)
|
return Math.round(v)
|
||||||
},
|
},
|
||||||
|
|
||||||
round2dm: function (v) {
|
round2dm(v) {
|
||||||
return Math.round(v * 100) / 100
|
return Math.round(v * 100) / 100
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollToWarnings: function () {
|
scrollToWarnings() {
|
||||||
if (!this.$refs.warnings) {
|
if (!this.$refs.warnings) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -312,9 +299,9 @@ export default {
|
|||||||
<div class="mt-5 mb-3">
|
<div class="mt-5 mb-3">
|
||||||
<div class="row w-100">
|
<div class="row w-100">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<Donut :sections="graphSections" background="var(--bs-body-bg)" :size="180" unit="px" :thickness="20"
|
<Donut :sections="graphSections" background="var(--bs-body-bg)" :size="180" unit="px"
|
||||||
has-legend legend-placement="bottom" :total="100" :start-angle="0" :auto-adjust-text-size="true"
|
:thickness="20" has-legend legend-placement="bottom" :total="100" :start-angle="0"
|
||||||
@section-click="scrollToWarnings">
|
:auto-adjust-text-size="true" @section-click="scrollToWarnings">
|
||||||
<h2 class="m-0" :class="scoreColor" @click="scrollToWarnings">
|
<h2 class="m-0" :class="scoreColor" @click="scrollToWarnings">
|
||||||
{{ round2dm(summary.Total.Supported) }}%
|
{{ round2dm(summary.Total.Supported) }}%
|
||||||
</h2>
|
</h2>
|
||||||
@ -388,8 +375,9 @@ export default {
|
|||||||
<div class="col-sm mt-2 mt-sm-0">
|
<div class="col-sm mt-2 mt-sm-0">
|
||||||
<div class="progress-stacked">
|
<div class="progress-stacked">
|
||||||
<div class="progress" role="progressbar" aria-label="Supported"
|
<div class="progress" role="progressbar" aria-label="Supported"
|
||||||
:aria-valuenow="warning.Score.Supported" aria-valuemin="0" aria-valuemax="100"
|
:aria-valuenow="warning.Score.Supported" aria-valuemin="0"
|
||||||
:style="{ width: warning.Score.Supported + '%' }" title="Supported">
|
aria-valuemax="100" :style="{ width: warning.Score.Supported + '%' }"
|
||||||
|
title="Supported">
|
||||||
<div class="progress-bar bg-success">
|
<div class="progress-bar bg-success">
|
||||||
{{ round(warning.Score.Supported) + '%' }}
|
{{ round(warning.Score.Supported) + '%' }}
|
||||||
</div>
|
</div>
|
||||||
@ -402,8 +390,9 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress" role="progressbar" aria-label="No"
|
<div class="progress" role="progressbar" aria-label="No"
|
||||||
:aria-valuenow="warning.Score.Unsupported" aria-valuemin="0" aria-valuemax="100"
|
:aria-valuenow="warning.Score.Unsupported" aria-valuemin="0"
|
||||||
:style="{ width: warning.Score.Unsupported + '%' }" title="Not supported">
|
aria-valuemax="100" :style="{ width: warning.Score.Unsupported + '%' }"
|
||||||
|
title="Not supported">
|
||||||
<div class="progress-bar bg-danger">
|
<div class="progress-bar bg-danger">
|
||||||
{{ round(warning.Score.Unsupported) + '%' }}
|
{{ round(warning.Score.Unsupported) + '%' }}
|
||||||
</div>
|
</div>
|
||||||
@ -420,7 +409,8 @@ export default {
|
|||||||
<i class="bi bi-info-circle me-2"></i>
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
Detected {{ warning.Score.Found }} <code>{{ warning.Title }}</code>
|
Detected {{ warning.Score.Found }} <code>{{ warning.Title }}</code>
|
||||||
propert<template v-if="warning.Score.Found === 1">y</template><template
|
propert<template v-if="warning.Score.Found === 1">y</template><template
|
||||||
v-else>ies</template> in the CSS styles, but unable to test if used or not.
|
v-else>ies</template> in the CSS
|
||||||
|
styles, but unable to test if used or not.
|
||||||
</span>
|
</span>
|
||||||
<span v-if="warning.Description != ''" v-html="warning.Description" class="me-2"></span>
|
<span v-if="warning.Description != ''" v-html="warning.Description" class="me-2"></span>
|
||||||
</p>
|
</p>
|
||||||
@ -487,9 +477,9 @@ export default {
|
|||||||
<div id="col1" class="accordion-collapse collapse"
|
<div id="col1" class="accordion-collapse collapse"
|
||||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
The support for HTML/CSS messages varies greatly across email clients. HTML check
|
The support for HTML/CSS messages varies greatly across email clients. HTML
|
||||||
attempts to calculate the overall support for your email for all selected platforms
|
check attempts to calculate the overall support for your email for all selected
|
||||||
to give you some idea of the general compatibility of your HTML email.
|
platforms to give you some idea of the general compatibility of your HTML email.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -507,29 +497,31 @@ export default {
|
|||||||
Internally the original HTML message is run against
|
Internally the original HTML message is run against
|
||||||
<b>{{ check.Total.Tests }}</b> different HTML and CSS tests. All tests
|
<b>{{ check.Total.Tests }}</b> different HTML and CSS tests. All tests
|
||||||
(except for <code><script></code>) correspond to a test on
|
(except for <code><script></code>) correspond to a test on
|
||||||
<a href="https://www.caniemail.com/" target="_blank">caniemail.com</a>, and the
|
<a href="https://www.caniemail.com/" target="_blank">caniemail.com</a>, and
|
||||||
final score is calculated using the available compatibility data.
|
the final score is calculated using the available compatibility data.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
CSS support is very difficult to programmatically test, especially if a message
|
CSS support is very difficult to programmatically test, especially if a
|
||||||
contains CSS style blocks or is linked to remote stylesheets. Remote stylesheets
|
message contains CSS style blocks or is linked to remote stylesheets. Remote
|
||||||
are, unless blocked via <code>--block-remote-css-and-fonts</code>, downloaded
|
stylesheets are, unless blocked via
|
||||||
and injected into the message as style blocks. The email is then
|
<code>--block-remote-css-and-fonts</code>,
|
||||||
<a href="https://github.com/vanng822/go-premailer" target="_blank">inlined</a>
|
downloaded and injected into the message as style blocks. The email is then
|
||||||
|
<a href="https://github.com/vanng822/go-premailer"
|
||||||
|
target="_blank">inlined</a>
|
||||||
to matching HTML elements. This gives Mailpit fairly accurate results.
|
to matching HTML elements. This gives Mailpit fairly accurate results.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
CSS properties such as <code>@font-face</code>, <code>:visited</code>,
|
CSS properties such as <code>@font-face</code>, <code>:visited</code>,
|
||||||
<code>:hover</code> etc cannot be inlined however, so these are searched for
|
<code>:hover</code> etc cannot be inlined however, so these are searched for
|
||||||
within CSS blocks. This method is not accurate as Mailpit does not know how many
|
within CSS blocks. This method is not accurate as Mailpit does not know how
|
||||||
nodes it actually applies to, if any, so they are weighted lightly (5%) as not
|
many nodes it actually applies to, if any, so they are weighted lightly (5%)
|
||||||
to affect the score. An example of this would be any email linking to the full
|
as not to affect the score. An example of this would be any email linking to
|
||||||
bootstrap CSS which contains dozens of unused attributes.
|
the full bootstrap CSS which contains dozens of unused attributes.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
All warnings are displayed with their respective support, including any specific
|
All warnings are displayed with their respective support, including any
|
||||||
notes, and it is up to you to decide what you do with that information and how
|
specific notes, and it is up to you to decide what you do with that
|
||||||
badly it may impact your message.
|
information and how badly it may impact your message.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -552,13 +544,15 @@ export default {
|
|||||||
<p>
|
<p>
|
||||||
For each test, Mailpit calculates both the unsupported & partially-supported
|
For each test, Mailpit calculates both the unsupported & partially-supported
|
||||||
percentages in relation to the number of matches against the total number of
|
percentages in relation to the number of matches against the total number of
|
||||||
nodes (elements) in the HTML. The maximum unsupported and partially-supported
|
nodes (elements) in the HTML. The maximum unsupported and
|
||||||
weighted scores are then used for the final score (ie: worst case scenario).
|
partially-supported weighted scores are then used for the final score (ie:
|
||||||
|
worst case scenario).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To try explain this logic in very simple terms: Assuming a
|
To try explain this logic in very simple terms: Assuming a
|
||||||
<code><script></code> node (element) has 100% failure (not supported in
|
<code><script></code> node (element) has 100% failure (not supported
|
||||||
any email client), and a <code><p></code> node has 100% pass (supported).
|
in any email client), and a <code><p></code> node has 100% pass
|
||||||
|
(supported).
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@ -575,7 +569,8 @@ export default {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
Mailpit will sort the warnings according to their weighted unsupported scores.
|
Mailpit will sort the warnings according to their weighted unsupported
|
||||||
|
scores.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -591,9 +586,9 @@ export default {
|
|||||||
<div id="col4" class="accordion-collapse collapse"
|
<div id="col4" class="accordion-collapse collapse"
|
||||||
data-bs-parent="#HTMLCheckAboutAccordion">
|
data-bs-parent="#HTMLCheckAboutAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
HTML check does not detect if the original HTML is valid. In order to detect applied
|
HTML check does not detect if the original HTML is valid. In order to detect
|
||||||
styles to every node, the HTML email is run through a parser which is very good at
|
applied styles to every node, the HTML email is run through a parser which is
|
||||||
turning invalid input into valid output. It is what it is...
|
very good at turning invalid input into valid output. It is what it is...
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import commonMixins from '../../mixins/CommonMixins'
|
import commonMixins from '../../mixins/CommonMixins'
|
||||||
|
|
||||||
@ -16,10 +15,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
let self = this;
|
let uri = this.resolve('/api/v1/message/' + this.message.ID + '/headers')
|
||||||
let uri = self.resolve('/api/v1/message/' + self.message.ID + '/headers')
|
this.get(uri, false, (response) => {
|
||||||
self.get(uri, false, function (response) {
|
this.headers = response.data
|
||||||
self.headers = response.data
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
groupedStatuses: function () {
|
groupedStatuses() {
|
||||||
let results = {}
|
let results = {}
|
||||||
|
|
||||||
if (!this.check) {
|
if (!this.check) {
|
||||||
@ -114,7 +114,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
doCheck: function () {
|
doCheck() {
|
||||||
this.check = false
|
this.check = false
|
||||||
this.loading = true
|
this.loading = true
|
||||||
let uri = this.resolve('/api/v1/message/' + this.message.ID + '/link-check')
|
let uri = this.resolve('/api/v1/message/' + this.message.ID + '/link-check')
|
||||||
@ -122,38 +122,37 @@ export default {
|
|||||||
uri += '?follow=true'
|
uri += '?follow=true'
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this
|
|
||||||
// ignore any error, do not show loader
|
// ignore any error, do not show loader
|
||||||
axios.get(uri, null)
|
axios.get(uri, null)
|
||||||
.then(function (result) {
|
.then((result) => {
|
||||||
self.check = result.data
|
this.check = result.data
|
||||||
self.error = false
|
this.error = false
|
||||||
|
|
||||||
self.$emit('setLinkErrors', result.data.Errors)
|
this.$emit('setLinkErrors', result.data.Errors)
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
// handle error
|
// handle error
|
||||||
if (error.response && error.response.data) {
|
if (error.response && error.response.data) {
|
||||||
// The request was made and the server responded with a status code
|
// The request was made and the server responded with a status code
|
||||||
// that falls out of the range of 2xx
|
// that falls out of the range of 2xx
|
||||||
if (error.response.data.Error) {
|
if (error.response.data.Error) {
|
||||||
self.error = error.response.data.Error
|
this.error = error.response.data.Error
|
||||||
} else {
|
} else {
|
||||||
self.error = error.response.data
|
this.error = error.response.data
|
||||||
}
|
}
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
// The request was made but no response was received
|
// The request was made but no response was received
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
// http.ClientRequest in node.js
|
// http.ClientRequest in node.js
|
||||||
self.error = 'Error sending data to the server. Please try again.'
|
this.error = 'Error sending data to the server. Please try again.'
|
||||||
} else {
|
} else {
|
||||||
// Something happened in setting up the request that triggered an Error
|
// Something happened in setting up the request that triggered an Error
|
||||||
self.error = error.message
|
this.error = error.message
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function (result) {
|
.then((result) => {
|
||||||
// always run
|
// always run
|
||||||
self.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -239,7 +238,8 @@ export default {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="LinkCheckOptions" tabindex="-1" aria-labelledby="LinkCheckOptionsLabel" aria-hidden="true">
|
<div class="modal fade" id="LinkCheckOptions" tabindex="-1" aria-labelledby="LinkCheckOptionsLabel"
|
||||||
|
aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -296,11 +296,12 @@ export default {
|
|||||||
What is Link check?
|
What is Link check?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="col1" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
<div id="col1" class="accordion-collapse collapse"
|
||||||
|
data-bs-parent="#LinkCheckAboutAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
Link check scans your message HTML and text for all unique links, images and linked
|
Link check scans your message HTML and text for all unique links, images and linked
|
||||||
stylesheets. It then does a HTTP <code>HEAD</code> request to each link, 5 at a time, to
|
stylesheets. It then does a HTTP <code>HEAD</code> request to each link, 5 at a
|
||||||
test whether the link/image/stylesheet exists.
|
time, to test whether the link/image/stylesheet exists.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -311,14 +312,16 @@ export default {
|
|||||||
What are "301" and "302" links?
|
What are "301" and "302" links?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="col2" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
<div id="col2" class="accordion-collapse collapse"
|
||||||
|
data-bs-parent="#LinkCheckAboutAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<p>
|
<p>
|
||||||
These are links that redirect you to another URL, for example newsletters
|
These are links that redirect you to another URL, for example newsletters
|
||||||
often use redirect links to track user clicks.
|
often use redirect links to track user clicks.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
By default Link check will not follow these links, however you can turn this on via
|
By default Link check will not follow these links, however you can turn this on
|
||||||
|
via
|
||||||
the settings and Link check will "follow" those redirects.
|
the settings and Link check will "follow" those redirects.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -331,7 +334,8 @@ export default {
|
|||||||
Why are some links returning an error but work in my browser?
|
Why are some links returning an error but work in my browser?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="col3" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
<div id="col3" class="accordion-collapse collapse"
|
||||||
|
data-bs-parent="#LinkCheckAboutAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<p>This may be due to various reasons, for instance:</p>
|
<p>This may be due to various reasons, for instance:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -353,11 +357,12 @@ export default {
|
|||||||
What are the risks of running Link check automatically?
|
What are the risks of running Link check automatically?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="col4" class="accordion-collapse collapse" data-bs-parent="#LinkCheckAboutAccordion">
|
<div id="col4" class="accordion-collapse collapse"
|
||||||
|
data-bs-parent="#LinkCheckAboutAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<p>
|
<p>
|
||||||
Depending on the type of messages you are testing, opening all links on all messages
|
Depending on the type of messages you are testing, opening all links on all
|
||||||
may have undesired consequences:
|
messages may have undesired consequences:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>If the message contains tracking links this may reveal your identity.</li>
|
<li>If the message contains tracking links this may reveal your identity.</li>
|
||||||
@ -366,13 +371,13 @@ export default {
|
|||||||
unsubscribe you.
|
unsubscribe you.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
To speed up the checking process, Link check will attempt 5 URLs at a time. This
|
To speed up the checking process, Link check will attempt 5 URLs at a time.
|
||||||
could lead to temporary heady load on the remote server.
|
This could lead to temporary heady load on the remote server.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
Unless you know what messages you receive, it is advised to only run the Link check
|
Unless you know what messages you receive, it is advised to only run the Link
|
||||||
manually.
|
check manually.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,16 +61,15 @@ export default {
|
|||||||
|
|
||||||
scaleHTMLPreview(v) {
|
scaleHTMLPreview(v) {
|
||||||
if (v == 'display') {
|
if (v == 'display') {
|
||||||
let self = this
|
window.setTimeout(() => {
|
||||||
window.setTimeout(function () {
|
this.resizeIFrames()
|
||||||
self.resizeIFrames()
|
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
hasAnyChecksEnabled: function () {
|
hasAnyChecksEnabled() {
|
||||||
return (mailbox.showHTMLCheck && this.message.HTML)
|
return (mailbox.showHTMLCheck && this.message.HTML)
|
||||||
|| mailbox.showLinkCheck
|
|| mailbox.showLinkCheck
|
||||||
|| (mailbox.showSpamCheck && mailbox.uiConfig.SpamAssassin)
|
|| (mailbox.showSpamCheck && mailbox.uiConfig.SpamAssassin)
|
||||||
@ -78,56 +77,53 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
let self = this
|
this.canSaveTags = false
|
||||||
self.canSaveTags = false
|
this.messageTags = this.message.Tags
|
||||||
self.messageTags = self.message.Tags
|
this.renderUI()
|
||||||
self.renderUI()
|
|
||||||
|
|
||||||
window.addEventListener("resize", self.resizeIFrames)
|
window.addEventListener("resize", this.resizeIFrames)
|
||||||
|
|
||||||
let headersTab = document.getElementById('nav-headers-tab')
|
let headersTab = document.getElementById('nav-headers-tab')
|
||||||
headersTab.addEventListener('shown.bs.tab', function (event) {
|
headersTab.addEventListener('shown.bs.tab', (event) => {
|
||||||
self.loadHeaders = true
|
this.loadHeaders = true
|
||||||
})
|
})
|
||||||
|
|
||||||
let rawTab = document.getElementById('nav-raw-tab')
|
let rawTab = document.getElementById('nav-raw-tab')
|
||||||
rawTab.addEventListener('shown.bs.tab', function (event) {
|
rawTab.addEventListener('shown.bs.tab', (event) => {
|
||||||
self.srcURI = self.resolve('/api/v1/message/' + self.message.ID + '/raw')
|
this.srcURI = this.resolve('/api/v1/message/' + this.message.ID + '/raw')
|
||||||
self.resizeIFrames()
|
this.resizeIFrames()
|
||||||
})
|
})
|
||||||
|
|
||||||
// manually refresh tags
|
// manually refresh tags
|
||||||
self.get(self.resolve(`/api/v1/tags`), false, function (response) {
|
this.get(this.resolve(`/api/v1/tags`), false, (response) => {
|
||||||
self.availableTags = response.data
|
this.availableTags = response.data
|
||||||
self.$nextTick(function () {
|
this.$nextTick(() => {
|
||||||
Tags.init('select[multiple]')
|
Tags.init('select[multiple]')
|
||||||
// delay tag change detection to allow Tags to load
|
// delay tag change detection to allow Tags to load
|
||||||
window.setTimeout(function () {
|
window.setTimeout(() => {
|
||||||
self.canSaveTags = true
|
this.canSaveTags = true
|
||||||
}, 200)
|
}, 200)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
isHTMLTabSelected: function () {
|
isHTMLTabSelected() {
|
||||||
this.showMobileButtons = this.$refs.navhtml
|
this.showMobileButtons = this.$refs.navhtml
|
||||||
&& this.$refs.navhtml.classList.contains('active')
|
&& this.$refs.navhtml.classList.contains('active')
|
||||||
},
|
},
|
||||||
|
|
||||||
renderUI: function () {
|
renderUI() {
|
||||||
let self = this
|
|
||||||
|
|
||||||
// activate the first non-disabled tab
|
// activate 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
|
document.getElementById('message-view').scrollTop = 0
|
||||||
|
|
||||||
self.isHTMLTabSelected()
|
this.isHTMLTabSelected()
|
||||||
|
|
||||||
document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(function (listObj) {
|
document.querySelectorAll('button[data-bs-toggle="tab"]').forEach((listObj) => {
|
||||||
listObj.addEventListener('shown.bs.tab', function (event) {
|
listObj.addEventListener('shown.bs.tab', (event) => {
|
||||||
self.isHTMLTabSelected()
|
this.isHTMLTabSelected()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -135,7 +131,7 @@ export default {
|
|||||||
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
[...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
||||||
|
|
||||||
// delay 0.2s until vue has rendered the iframe content
|
// delay 0.2s until vue has rendered the iframe content
|
||||||
window.setTimeout(function () {
|
window.setTimeout(() => {
|
||||||
let p = document.getElementById('preview-html')
|
let p = document.getElementById('preview-html')
|
||||||
if (p) {
|
if (p) {
|
||||||
// make links open in new window
|
// make links open in new window
|
||||||
@ -148,7 +144,7 @@ export default {
|
|||||||
anchorEl.setAttribute('target', '_blank')
|
anchorEl.setAttribute('target', '_blank')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.resizeIFrames()
|
this.resizeIFrames()
|
||||||
}
|
}
|
||||||
}, 200)
|
}, 200)
|
||||||
|
|
||||||
@ -158,12 +154,12 @@ export default {
|
|||||||
Prism.highlightAll()
|
Prism.highlightAll()
|
||||||
},
|
},
|
||||||
|
|
||||||
resizeIframe: function (el) {
|
resizeIframe(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'
|
||||||
},
|
},
|
||||||
|
|
||||||
resizeIFrames: function () {
|
resizeIFrames() {
|
||||||
if (this.scaleHTMLPreview != 'display') {
|
if (this.scaleHTMLPreview != 'display') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -175,7 +171,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// set the iframe body & text colors based on current theme
|
// set the iframe body & text colors based on current theme
|
||||||
initRawIframe: function (el) {
|
initRawIframe(el) {
|
||||||
let bodyStyles = window.getComputedStyle(document.body, null)
|
let bodyStyles = window.getComputedStyle(document.body, null)
|
||||||
let bg = bodyStyles.getPropertyValue('background-color')
|
let bg = bodyStyles.getPropertyValue('background-color')
|
||||||
let txt = bodyStyles.getPropertyValue('color')
|
let txt = bodyStyles.getPropertyValue('color')
|
||||||
@ -189,27 +185,25 @@ export default {
|
|||||||
this.resizeIframe(el)
|
this.resizeIframe(el)
|
||||||
},
|
},
|
||||||
|
|
||||||
sanitizeHTML: function (h) {
|
sanitizeHTML(h) {
|
||||||
// remove <base/> tag if set
|
// remove <base/> tag if set
|
||||||
return h.replace(/<base .*>/mi, '')
|
return h.replace(/<base .*>/mi, '')
|
||||||
},
|
},
|
||||||
|
|
||||||
saveTags: function () {
|
saveTags() {
|
||||||
let self = this
|
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
IDs: [this.message.ID],
|
IDs: [this.message.ID],
|
||||||
Tags: this.messageTags
|
Tags: this.messageTags
|
||||||
}
|
}
|
||||||
|
|
||||||
self.put(self.resolve('/api/v1/tags'), data, function (response) {
|
this.put(this.resolve('/api/v1/tags'), data, (response) => {
|
||||||
window.scrollInPlace = true
|
window.scrollInPlace = true
|
||||||
self.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Convert plain text to HTML including anchor links
|
// Convert plain text to HTML including anchor links
|
||||||
textToHTML: function (s) {
|
textToHTML(s) {
|
||||||
let html = s
|
let html = s
|
||||||
|
|
||||||
// full links with http(s)
|
// full links with http(s)
|
||||||
|
@ -46,26 +46,25 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// triggered manually after modal is shown
|
// triggered manually after modal is shown
|
||||||
initTags: function () {
|
initTags() {
|
||||||
Tags.init("select[multiple]")
|
Tags.init("select[multiple]")
|
||||||
},
|
},
|
||||||
|
|
||||||
releaseMessage: function () {
|
releaseMessage() {
|
||||||
let self = this
|
|
||||||
// set timeout to allow for user clicking send before the tag filter has applied the tag
|
// set timeout to allow for user clicking send before the tag filter has applied the tag
|
||||||
window.setTimeout(function () {
|
window.setTimeout(() => {
|
||||||
if (!self.addresses.length) {
|
if (!this.addresses.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
To: self.addresses
|
To: this.addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
self.post(self.resolve('/api/v1/message/' + self.message.ID + '/release'), data, function (response) {
|
this.post(this.resolve('/api/v1/message/' + this.message.ID + '/release'), data, (response) => {
|
||||||
self.modal("ReleaseModal").hide()
|
this.modal("ReleaseModal").hide()
|
||||||
if (self.deleteAfterRelease) {
|
if (this.deleteAfterRelease) {
|
||||||
self.$emit('delete')
|
this.$emit('delete')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 100)
|
}, 100)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AjaxLoader from '../AjaxLoader.vue'
|
import AjaxLoader from '../AjaxLoader.vue'
|
||||||
import CommonMixins from '../../mixins/CommonMixins'
|
import CommonMixins from '../../mixins/CommonMixins'
|
||||||
@ -23,9 +22,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
initScreenshot: function () {
|
initScreenshot() {
|
||||||
this.loading = 1
|
this.loading = 1
|
||||||
let self = this
|
|
||||||
// remove base tag, if set
|
// remove base tag, if set
|
||||||
let h = this.message.HTML.replace(/<base .*>/mi, '')
|
let h = this.message.HTML.replace(/<base .*>/mi, '')
|
||||||
let proxy = this.resolve('/proxy')
|
let proxy = this.resolve('/proxy')
|
||||||
@ -38,11 +36,11 @@ export default {
|
|||||||
|
|
||||||
// update any inline `url(...)` absolute links
|
// update any inline `url(...)` absolute links
|
||||||
const urlRegex = /(url\((\'|\")?(https?:\/\/[^\)\'\"]+)(\'|\")?\))/mgi;
|
const urlRegex = /(url\((\'|\")?(https?:\/\/[^\)\'\"]+)(\'|\")?\))/mgi;
|
||||||
h = h.replaceAll(urlRegex, function (match, p1, p2, p3) {
|
h = h.replaceAll(urlRegex, (match, p1, p2, p3) => {
|
||||||
if (typeof p2 === 'string') {
|
if (typeof p2 === 'string') {
|
||||||
return `url(${p2}${proxy}?url=` + encodeURIComponent(self.decodeEntities(p3)) + `${p2})`
|
return `url(${p2}${proxy}?url=` + encodeURIComponent(this.decodeEntities(p3)) + `${p2})`
|
||||||
}
|
}
|
||||||
return `url(${proxy}?url=` + encodeURIComponent(self.decodeEntities(p3)) + `)`
|
return `url(${proxy}?url=` + encodeURIComponent(this.decodeEntities(p3)) + `)`
|
||||||
})
|
})
|
||||||
|
|
||||||
// create temporary document to manipulate
|
// create temporary document to manipulate
|
||||||
@ -63,7 +61,7 @@ export default {
|
|||||||
let src = i.getAttribute('href')
|
let src = i.getAttribute('href')
|
||||||
|
|
||||||
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
||||||
i.setAttribute('href', `${proxy}?url=` + encodeURIComponent(self.decodeEntities(src)))
|
i.setAttribute('href', `${proxy}?url=` + encodeURIComponent(this.decodeEntities(src)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +70,7 @@ export default {
|
|||||||
for (let i of images) {
|
for (let i of images) {
|
||||||
let src = i.getAttribute('src')
|
let src = i.getAttribute('src')
|
||||||
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
||||||
i.setAttribute('src', `${proxy}?url=` + encodeURIComponent(self.decodeEntities(src)))
|
i.setAttribute('src', `${proxy}?url=` + encodeURIComponent(this.decodeEntities(src)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +81,7 @@ export default {
|
|||||||
|
|
||||||
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
if (src && src.match(/^https?:\/\//i) && src.indexOf(window.location.origin + window.location.pathname) !== 0) {
|
||||||
// replace with proxy link
|
// replace with proxy link
|
||||||
i.setAttribute('background', `${proxy}?url=` + encodeURIComponent(self.decodeEntities(src)))
|
i.setAttribute('background', `${proxy}?url=` + encodeURIComponent(this.decodeEntities(src)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +90,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// HTML decode function
|
// HTML decode function
|
||||||
decodeEntities: function (s) {
|
decodeEntities(s) {
|
||||||
let e = document.createElement('div')
|
let e = document.createElement('div')
|
||||||
e.innerHTML = s
|
e.innerHTML = s
|
||||||
let str = e.textContent
|
let str = e.textContent
|
||||||
@ -100,8 +98,7 @@ export default {
|
|||||||
return str
|
return str
|
||||||
},
|
},
|
||||||
|
|
||||||
doScreenshot: function () {
|
doScreenshot() {
|
||||||
let self = this
|
|
||||||
let width = document.getElementById('message-view').getBoundingClientRect().width
|
let width = document.getElementById('message-view').getBoundingClientRect().width
|
||||||
|
|
||||||
let prev = document.getElementById('preview-html')
|
let prev = document.getElementById('preview-html')
|
||||||
@ -113,7 +110,7 @@ export default {
|
|||||||
width = 300
|
width = 300
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = document.getElementById('screenshot-html')
|
const i = document.getElementById('screenshot-html')
|
||||||
|
|
||||||
// set the iframe width
|
// set the iframe width
|
||||||
i.style.width = width + 'px'
|
i.style.width = width + 'px'
|
||||||
@ -127,11 +124,11 @@ export default {
|
|||||||
width: width,
|
width: width,
|
||||||
}).then(dataUrl => {
|
}).then(dataUrl => {
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.download = self.message.ID + '.png'
|
link.download = this.message.ID + '.png'
|
||||||
link.href = dataUrl
|
link.href = dataUrl
|
||||||
link.click()
|
link.click()
|
||||||
self.loading = 0
|
this.loading = 0
|
||||||
self.html = false
|
this.html = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,41 +38,39 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
doCheck: function () {
|
doCheck() {
|
||||||
this.check = false
|
this.check = false
|
||||||
|
|
||||||
let self = this
|
|
||||||
|
|
||||||
// ignore any error, do not show loader
|
// ignore any error, do not show loader
|
||||||
axios.get(self.resolve('/api/v1/message/' + self.message.ID + '/sa-check'), null)
|
axios.get(this.resolve('/api/v1/message/' + this.message.ID + '/sa-check'), null)
|
||||||
.then(function (result) {
|
.then((result) => {
|
||||||
self.check = result.data
|
this.check = result.data
|
||||||
self.error = false
|
this.error = false
|
||||||
self.setIcons()
|
this.setIcons()
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
// handle error
|
// handle error
|
||||||
if (error.response && error.response.data) {
|
if (error.response && error.response.data) {
|
||||||
// The request was made and the server responded with a status code
|
// The request was made and the server responded with a status code
|
||||||
// that falls out of the range of 2xx
|
// that falls out of the range of 2xx
|
||||||
if (error.response.data.Error) {
|
if (error.response.data.Error) {
|
||||||
self.error = error.response.data.Error
|
this.error = error.response.data.Error
|
||||||
} else {
|
} else {
|
||||||
self.error = error.response.data
|
this.error = error.response.data
|
||||||
}
|
}
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
// The request was made but no response was received
|
// The request was made but no response was received
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
// http.ClientRequest in node.js
|
// http.ClientRequest in node.js
|
||||||
self.error = 'Error sending data to the server. Please try again.'
|
this.error = 'Error sending data to the server. Please try again.'
|
||||||
} else {
|
} else {
|
||||||
// Something happened in setting up the request that triggered an Error
|
// Something happened in setting up the request that triggered an Error
|
||||||
self.error = error.message
|
this.error = error.message
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
badgeStyle: function (ignorePadding = false) {
|
badgeStyle(ignorePadding = false) {
|
||||||
let badgeStyle = 'bg-success'
|
let badgeStyle = 'bg-success'
|
||||||
if (this.check.Error) {
|
if (this.check.Error) {
|
||||||
badgeStyle = 'bg-warning text-primary'
|
badgeStyle = 'bg-warning text-primary'
|
||||||
@ -90,7 +88,7 @@ export default {
|
|||||||
return badgeStyle
|
return badgeStyle
|
||||||
},
|
},
|
||||||
|
|
||||||
setIcons: function () {
|
setIcons() {
|
||||||
let score = this.check.Score
|
let score = this.check.Score
|
||||||
if (this.check.Error && this.check.Error != '') {
|
if (this.check.Error && this.check.Error != '') {
|
||||||
score = '!'
|
score = '!'
|
||||||
@ -102,7 +100,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
graphSections: function () {
|
graphSections() {
|
||||||
let score = this.check.Score
|
let score = this.check.Score
|
||||||
let p = Math.round(score / 5 * 100)
|
let p = Math.round(score / 5 * 100)
|
||||||
if (p > 100) {
|
if (p > 100) {
|
||||||
@ -125,7 +123,7 @@ export default {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
scoreColor: function () {
|
scoreColor() {
|
||||||
return this.graphSections[0].color
|
return this.graphSections[0].color
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import axios from 'axios'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import ColorHash from 'color-hash'
|
import ColorHash from 'color-hash'
|
||||||
import { Modal, Offcanvas } from 'bootstrap'
|
import { Modal, Offcanvas } from 'bootstrap'
|
||||||
import {limitOptions} from "../stores/pagination";
|
import { limitOptions } from "../stores/pagination";
|
||||||
|
|
||||||
// BootstrapElement is used to return a fake Bootstrap element
|
// BootstrapElement is used to return a fake Bootstrap element
|
||||||
// if the ID returns nothing to prevent errors.
|
// if the ID returns nothing to prevent errors.
|
||||||
@ -25,15 +25,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
resolve: function (u) {
|
resolve(u) {
|
||||||
return this.$router.resolve(u).href
|
return this.$router.resolve(u).href
|
||||||
},
|
},
|
||||||
|
|
||||||
searchURI: function (s) {
|
searchURI(s) {
|
||||||
return this.resolve('/search') + '?q=' + encodeURIComponent(s)
|
return this.resolve('/search') + '?q=' + encodeURIComponent(s)
|
||||||
},
|
},
|
||||||
|
|
||||||
getFileSize: function (bytes) {
|
getFileSize(bytes) {
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
return '0B'
|
return '0B'
|
||||||
}
|
}
|
||||||
@ -41,19 +41,19 @@ export default {
|
|||||||
return (bytes / Math.pow(1024, i)).toFixed(1) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
|
return (bytes / Math.pow(1024, i)).toFixed(1) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
|
||||||
},
|
},
|
||||||
|
|
||||||
formatNumber: function (nr) {
|
formatNumber(nr) {
|
||||||
return new Intl.NumberFormat().format(nr)
|
return new Intl.NumberFormat().format(nr)
|
||||||
},
|
},
|
||||||
|
|
||||||
messageDate: function (d) {
|
messageDate(d) {
|
||||||
return dayjs(d).format('ddd, D MMM YYYY, h:mm a')
|
return dayjs(d).format('ddd, D MMM YYYY, h:mm a')
|
||||||
},
|
},
|
||||||
|
|
||||||
secondsToRelative: function (d) {
|
secondsToRelative(d) {
|
||||||
return dayjs().subtract(d, 'seconds').fromNow()
|
return dayjs().subtract(d, 'seconds').fromNow()
|
||||||
},
|
},
|
||||||
|
|
||||||
tagEncodeURI: function (tag) {
|
tagEncodeURI(tag) {
|
||||||
if (tag.match(/ /)) {
|
if (tag.match(/ /)) {
|
||||||
tag = `"${tag}"`
|
tag = `"${tag}"`
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ export default {
|
|||||||
return encodeURIComponent(`tag:${tag}`)
|
return encodeURIComponent(`tag:${tag}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
getSearch: function () {
|
getSearch() {
|
||||||
if (!window.location.search) {
|
if (!window.location.search) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ export default {
|
|||||||
return q
|
return q
|
||||||
},
|
},
|
||||||
|
|
||||||
getPaginationParams: function () {
|
getPaginationParams() {
|
||||||
if (!window.location.search) {
|
if (!window.location.search) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -90,8 +90,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// generic modal get/set function
|
// generic modal get/set function
|
||||||
modal: function (id) {
|
modal(id) {
|
||||||
let e = document.getElementById(id)
|
const e = document.getElementById(id)
|
||||||
if (e) {
|
if (e) {
|
||||||
return Modal.getOrCreateInstance(e)
|
return Modal.getOrCreateInstance(e)
|
||||||
}
|
}
|
||||||
@ -100,8 +100,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// close mobile navigation
|
// close mobile navigation
|
||||||
hideNav: function () {
|
hideNav() {
|
||||||
let e = document.getElementById('offcanvas')
|
const e = document.getElementById('offcanvas')
|
||||||
if (e) {
|
if (e) {
|
||||||
Offcanvas.getOrCreateInstance(e).hide()
|
Offcanvas.getOrCreateInstance(e).hide()
|
||||||
}
|
}
|
||||||
@ -115,22 +115,21 @@ export default {
|
|||||||
* @params function callback function
|
* @params function callback function
|
||||||
* @params function error callback function
|
* @params function error callback function
|
||||||
*/
|
*/
|
||||||
get: function (url, values, callback, errorCallback) {
|
get(url, values, callback, errorCallback) {
|
||||||
let self = this
|
this.loading++
|
||||||
self.loading++
|
|
||||||
axios.get(url, { params: values })
|
axios.get(url, { params: values })
|
||||||
.then(callback)
|
.then(callback)
|
||||||
.catch(function (err) {
|
.catch((err) => {
|
||||||
if (typeof errorCallback == 'function') {
|
if (typeof errorCallback == 'function') {
|
||||||
return errorCallback(err)
|
return errorCallback(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handleError(err)
|
this.handleError(err)
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(() => {
|
||||||
// always executed
|
// always executed
|
||||||
if (self.loading > 0) {
|
if (this.loading > 0) {
|
||||||
self.loading--
|
this.loading--
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -142,16 +141,15 @@ export default {
|
|||||||
* @params array object/array values
|
* @params array object/array values
|
||||||
* @params function callback function
|
* @params function callback function
|
||||||
*/
|
*/
|
||||||
post: function (url, data, callback) {
|
post(url, data, callback) {
|
||||||
let self = this
|
this.loading++
|
||||||
self.loading++
|
|
||||||
axios.post(url, data)
|
axios.post(url, data)
|
||||||
.then(callback)
|
.then(callback)
|
||||||
.catch(self.handleError)
|
.catch(this.handleError)
|
||||||
.then(function () {
|
.then(() => {
|
||||||
// always executed
|
// always executed
|
||||||
if (self.loading > 0) {
|
if (this.loading > 0) {
|
||||||
self.loading--
|
this.loading--
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -163,16 +161,15 @@ export default {
|
|||||||
* @params array object/array values
|
* @params array object/array values
|
||||||
* @params function callback function
|
* @params function callback function
|
||||||
*/
|
*/
|
||||||
delete: function (url, data, callback) {
|
delete(url, data, callback) {
|
||||||
let self = this
|
this.loading++
|
||||||
self.loading++
|
|
||||||
axios.delete(url, { data: data })
|
axios.delete(url, { data: data })
|
||||||
.then(callback)
|
.then(callback)
|
||||||
.catch(self.handleError)
|
.catch(this.handleError)
|
||||||
.then(function () {
|
.then(() => {
|
||||||
// always executed
|
// always executed
|
||||||
if (self.loading > 0) {
|
if (this.loading > 0) {
|
||||||
self.loading--
|
this.loading--
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -184,22 +181,21 @@ export default {
|
|||||||
* @params array object/array values
|
* @params array object/array values
|
||||||
* @params function callback function
|
* @params function callback function
|
||||||
*/
|
*/
|
||||||
put: function (url, data, callback) {
|
put(url, data, callback) {
|
||||||
let self = this
|
this.loading++
|
||||||
self.loading++
|
|
||||||
axios.put(url, data)
|
axios.put(url, data)
|
||||||
.then(callback)
|
.then(callback)
|
||||||
.catch(self.handleError)
|
.catch(this.handleError)
|
||||||
.then(function () {
|
.then(() => {
|
||||||
// always executed
|
// always executed
|
||||||
if (self.loading > 0) {
|
if (this.loading > 0) {
|
||||||
self.loading--
|
this.loading--
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Ajax error message
|
// Ajax error message
|
||||||
handleError: function (error) {
|
handleError(error) {
|
||||||
// handle error
|
// handle error
|
||||||
if (error.response && error.response.data) {
|
if (error.response && error.response.data) {
|
||||||
// The request was made and the server responded with a status code
|
// The request was made and the server responded with a status code
|
||||||
@ -218,7 +214,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
allAttachments: function (message) {
|
allAttachments(message) {
|
||||||
let a = []
|
let a = []
|
||||||
for (let i in message.Attachments) {
|
for (let i in message.Attachments) {
|
||||||
a.push(message.Attachments[i])
|
a.push(message.Attachments[i])
|
||||||
@ -237,7 +233,7 @@ export default {
|
|||||||
return a.ContentType.match(/^image\//)
|
return a.ContentType.match(/^image\//)
|
||||||
},
|
},
|
||||||
|
|
||||||
attachmentIcon: function (a) {
|
attachmentIcon(a) {
|
||||||
let ext = a.FileName.split('.').pop().toLowerCase()
|
let ext = a.FileName.split('.').pop().toLowerCase()
|
||||||
|
|
||||||
if (a.ContentType.match(/^image\//)) {
|
if (a.ContentType.match(/^image\//)) {
|
||||||
@ -279,7 +275,7 @@ export default {
|
|||||||
|
|
||||||
// Returns a hex color based on a string.
|
// Returns a hex color based on a string.
|
||||||
// Values are stored in an array for faster lookup / processing.
|
// Values are stored in an array for faster lookup / processing.
|
||||||
colorHash: function (s) {
|
colorHash(s) {
|
||||||
if (this.tagColorCache[s] != undefined) {
|
if (this.tagColorCache[s] != undefined) {
|
||||||
return this.tagColorCache[s]
|
return this.tagColorCache[s]
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,12 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
reloadMailbox: function () {
|
reloadMailbox() {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
this.loadMessages()
|
this.loadMessages()
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMessages: function () {
|
loadMessages() {
|
||||||
if (!this.apiURI) {
|
if (!this.apiURI) {
|
||||||
alert('apiURL not set!')
|
alert('apiURL not set!')
|
||||||
return
|
return
|
||||||
@ -43,8 +43,7 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this
|
const params = {}
|
||||||
let params = {}
|
|
||||||
mailbox.selected = []
|
mailbox.selected = []
|
||||||
|
|
||||||
params['limit'] = pagination.limit
|
params['limit'] = pagination.limit
|
||||||
@ -52,7 +51,7 @@ export default {
|
|||||||
params['start'] = pagination.start
|
params['start'] = pagination.start
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get(this.apiURI, params, function (response) {
|
this.get(this.apiURI, params, (response) => {
|
||||||
mailbox.total = response.data.total // all messages
|
mailbox.total = response.data.total // all messages
|
||||||
mailbox.unread = response.data.unread // all unread messages
|
mailbox.unread = response.data.unread // all unread messages
|
||||||
mailbox.tags = response.data.tags // all tags
|
mailbox.tags = response.data.tags // all tags
|
||||||
@ -63,18 +62,18 @@ export default {
|
|||||||
|
|
||||||
if (response.data.count == 0 && response.data.start > 0) {
|
if (response.data.count == 0 && response.data.start > 0) {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
return self.loadMessages()
|
return this.loadMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mailbox.lastMessage) {
|
if (mailbox.lastMessage) {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
let m = document.getElementById(mailbox.lastMessage)
|
const m = document.getElementById(mailbox.lastMessage)
|
||||||
if (m) {
|
if (m) {
|
||||||
m.focus()
|
m.focus()
|
||||||
// m.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
// m.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||||
m.scrollIntoView({ block: 'center' })
|
m.scrollIntoView({ block: 'center' })
|
||||||
} else {
|
} else {
|
||||||
let mp = document.getElementById('message-page')
|
const mp = document.getElementById('message-page')
|
||||||
if (mp) {
|
if (mp) {
|
||||||
mp.scrollTop = 0
|
mp.scrollTop = 0
|
||||||
}
|
}
|
||||||
@ -84,7 +83,7 @@ export default {
|
|||||||
}, 50)
|
}, 50)
|
||||||
|
|
||||||
} else if (!window.scrollInPlace) {
|
} else if (!window.scrollInPlace) {
|
||||||
let mp = document.getElementById('message-page')
|
const mp = document.getElementById('message-page')
|
||||||
if (mp) {
|
if (mp) {
|
||||||
mp.scrollTop = 0
|
mp.scrollTop = 0
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadMailbox: function () {
|
loadMailbox() {
|
||||||
const paginationParams = this.getPaginationParams()
|
const paginationParams = this.getPaginationParams()
|
||||||
if (paginationParams?.start) {
|
if (paginationParams?.start) {
|
||||||
pagination.start = paginationParams.start
|
pagination.start = paginationParams.start
|
||||||
|
@ -41,16 +41,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadMessage: function () {
|
loadMessage() {
|
||||||
let self = this
|
|
||||||
this.message = false
|
this.message = false
|
||||||
let uri = self.resolve('/api/v1/message/' + this.$route.params.id)
|
const uri = this.resolve('/api/v1/message/' + this.$route.params.id)
|
||||||
self.get(uri, false, function (response) {
|
this.get(uri, false, (response) => {
|
||||||
self.errorMessage = false
|
this.errorMessage = false
|
||||||
|
const d = response.data
|
||||||
|
|
||||||
let d = response.data
|
if (this.wasUnread(d.ID)) {
|
||||||
|
|
||||||
if (self.wasUnread(d.ID)) {
|
|
||||||
mailbox.unread--
|
mailbox.unread--
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,14 +59,14 @@ export default {
|
|||||||
if (a.ContentID != '') {
|
if (a.ContentID != '') {
|
||||||
d.HTML = d.HTML.replace(
|
d.HTML = d.HTML.replace(
|
||||||
new RegExp('(=["\']?)(cid:' + a.ContentID + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
new RegExp('(=["\']?)(cid:' + a.ContentID + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
||||||
'$1' + self.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
'$1' + this.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
||||||
// some old email clients use the filename
|
// some old email clients use the filename
|
||||||
d.HTML = d.HTML.replace(
|
d.HTML = d.HTML.replace(
|
||||||
new RegExp('(=["\']?)(' + a.FileName + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
new RegExp('(=["\']?)(' + a.FileName + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
||||||
'$1' + self.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
'$1' + this.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,43 +79,43 @@ export default {
|
|||||||
if (a.ContentID != '') {
|
if (a.ContentID != '') {
|
||||||
d.HTML = d.HTML.replace(
|
d.HTML = d.HTML.replace(
|
||||||
new RegExp('(=["\']?)(cid:' + a.ContentID + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
new RegExp('(=["\']?)(cid:' + a.ContentID + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
||||||
'$1' + self.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
'$1' + this.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
if (a.FileName.match(/^[a-zA-Z0-9\_\-\.]+$/)) {
|
||||||
// some old email clients use the filename
|
// some old email clients use the filename
|
||||||
d.HTML = d.HTML.replace(
|
d.HTML = d.HTML.replace(
|
||||||
new RegExp('(=["\']?)(' + a.FileName + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
new RegExp('(=["\']?)(' + a.FileName + ')(["|\'|\\s|\\/|>|;])', 'g'),
|
||||||
'$1' + self.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
'$1' + this.resolve('/api/v1/message/' + d.ID + '/part/' + a.PartID) + '$3'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.message = d
|
this.message = d
|
||||||
|
|
||||||
self.detectPrevNext()
|
this.detectPrevNext()
|
||||||
},
|
},
|
||||||
function (error) {
|
(error) => {
|
||||||
self.errorMessage = true
|
this.errorMessage = true
|
||||||
if (error.response && error.response.data) {
|
if (error.response && error.response.data) {
|
||||||
if (error.response.data.Error) {
|
if (error.response.data.Error) {
|
||||||
self.errorMessage = error.response.data.Error
|
this.errorMessage = error.response.data.Error
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = error.response.data
|
this.errorMessage = error.response.data
|
||||||
}
|
}
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
// The request was made but no response was received
|
// The request was made but no response was received
|
||||||
self.errorMessage = 'Error sending data to the server. Please refresh the page.'
|
this.errorMessage = 'Error sending data to the server. Please refresh the page.'
|
||||||
} else {
|
} else {
|
||||||
// Something happened in setting up the request that triggered an Error
|
// Something happened in setting up the request that triggered an Error
|
||||||
self.errorMessage = error.message
|
this.errorMessage = error.message
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// try detect whether this message was unread based on messages listing
|
// try detect whether this message was unread based on messages listing
|
||||||
wasUnread: function (id) {
|
wasUnread(id) {
|
||||||
for (let m in mailbox.messages) {
|
for (let m in mailbox.messages) {
|
||||||
if (mailbox.messages[m].ID == id) {
|
if (mailbox.messages[m].ID == id) {
|
||||||
if (!mailbox.messages[m].Read) {
|
if (!mailbox.messages[m].Read) {
|
||||||
@ -129,7 +127,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
detectPrevNext: function () {
|
detectPrevNext() {
|
||||||
// generate the prev/next links based on current message list
|
// generate the prev/next links based on current message list
|
||||||
this.prevLink = false
|
this.prevLink = false
|
||||||
this.nextLink = false
|
this.nextLink = false
|
||||||
@ -147,40 +145,38 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadMessageBody: function (str, ext) {
|
downloadMessageBody(str, ext) {
|
||||||
let dl = document.createElement('a')
|
const dl = document.createElement('a')
|
||||||
dl.href = "data:text/plain," + encodeURIComponent(str)
|
dl.href = "data:text/plain," + encodeURIComponent(str)
|
||||||
dl.target = '_blank'
|
dl.target = '_blank'
|
||||||
dl.download = this.message.ID + '.' + ext
|
dl.download = this.message.ID + '.' + ext
|
||||||
dl.click()
|
dl.click()
|
||||||
},
|
},
|
||||||
|
|
||||||
screenshotMessageHTML: function () {
|
screenshotMessageHTML() {
|
||||||
this.$refs.ScreenshotRef.initScreenshot()
|
this.$refs.ScreenshotRef.initScreenshot()
|
||||||
},
|
},
|
||||||
|
|
||||||
// mark current message as read
|
// mark current message as read
|
||||||
markUnread: function () {
|
markUnread() {
|
||||||
let self = this
|
if (!this.message) {
|
||||||
if (!self.message) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let uri = self.resolve('/api/v1/messages')
|
const uri = this.resolve('/api/v1/messages')
|
||||||
self.put(uri, { 'read': false, 'ids': [self.message.ID] }, function (response) {
|
this.put(uri, { 'read': false, 'ids': [this.message.ID] }, (response) => {
|
||||||
self.goBack()
|
this.goBack()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteMessage: function () {
|
deleteMessage() {
|
||||||
let self = this
|
const ids = [this.message.ID]
|
||||||
let ids = [self.message.ID]
|
const uri = this.resolve('/api/v1/messages')
|
||||||
let uri = self.resolve('/api/v1/messages')
|
this.delete(uri, { 'ids': ids }, () => {
|
||||||
self.delete(uri, { 'ids': ids }, function () {
|
this.goBack()
|
||||||
self.goBack()
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
goBack: function () {
|
goBack() {
|
||||||
mailbox.lastMessage = this.$route.params.id
|
mailbox.lastMessage = this.$route.params.id
|
||||||
|
|
||||||
if (mailbox.searching) {
|
if (mailbox.searching) {
|
||||||
@ -208,16 +204,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initReleaseModal: function () {
|
initReleaseModal() {
|
||||||
let self = this
|
this.modal('ReleaseModal').show()
|
||||||
self.modal('ReleaseModal').show()
|
window.setTimeout(() => {
|
||||||
window.setTimeout(function () {
|
// delay to allow elements to load / focus
|
||||||
window.setTimeout(function () {
|
this.$refs.ReleaseRef.initTags()
|
||||||
// delay to allow elements to load / focus
|
document.querySelector('#ReleaseModal input[role="combobox"]').focus()
|
||||||
self.$refs.ReleaseRef.initTags()
|
}, 500)
|
||||||
document.querySelector('#ReleaseModal input[role="combobox"]').focus()
|
|
||||||
}, 500)
|
|
||||||
}, 300)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
doSearch: function () {
|
doSearch() {
|
||||||
let s = this.getSearch()
|
const s = this.getSearch()
|
||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
mailbox.searching = false
|
mailbox.searching = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user