mirror of
				https://github.com/axllent/mailpit.git
				synced 2025-10-31 00:07:43 +02:00 
			
		
		
		
	Feature: Download raw message, HTML/text body parts or attachments via single button
@see #67
This commit is contained in:
		| @@ -542,6 +542,14 @@ export default { | |||||||
| 				self.appInfo = response.data; | 				self.appInfo = response.data; | ||||||
| 				self.modal('AppInfoModal').show(); | 				self.modal('AppInfoModal').show(); | ||||||
| 			}); | 			}); | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		downloadMessageBody: function (str, ext) { | ||||||
|  | 			let dl = document.createElement('a'); | ||||||
|  | 			dl.href = "data:text/plain," + encodeURIComponent(str); | ||||||
|  | 			dl.target = '_blank'; | ||||||
|  | 			dl.download = this.message.ID + '.' + ext; | ||||||
|  | 			dl.click(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -575,10 +583,49 @@ export default { | |||||||
| 				:href="'#' + messagePrev" title="View previous message"> | 				:href="'#' + messagePrev" title="View previous message"> | ||||||
| 				<i class="bi bi-caret-left-fill"></i> | 				<i class="bi bi-caret-left-fill"></i> | ||||||
| 			</a> | 			</a> | ||||||
| 			<a :href="'api/v1/message/' + message.ID + '/raw?dl=1'" class="btn btn-outline-light me-2 float-end" | 			<div class="dropdown float-end" id="DownloadBtn"> | ||||||
| 				title="Download message"> | 				<button type="button" class="btn btn-outline-light dropdown-toggle" data-bs-toggle="dropdown" | ||||||
| 				<i class="bi bi-file-arrow-down-fill"></i> <span class="d-none d-md-inline">Download</span> | 					aria-expanded="false"> | ||||||
| 			</a> | 					<i class="bi bi-file-arrow-down-fill"></i> | ||||||
|  | 					<span class="d-none d-md-inline ms-1">Download</span> | ||||||
|  | 				</button> | ||||||
|  | 				<ul class="dropdown-menu dropdown-menu-end"> | ||||||
|  | 					<li> | ||||||
|  | 						<a :href="'api/v1/message/' + message.ID + '/raw?dl=1'" class="dropdown-item" | ||||||
|  | 							title="Message source including headers, body and attachments"> | ||||||
|  | 							Raw message | ||||||
|  | 						</a> | ||||||
|  | 					</li> | ||||||
|  | 					<li v-if="message.HTML"> | ||||||
|  | 						<button v-on:click="downloadMessageBody(message.HTML, 'html')" class="dropdown-item"> | ||||||
|  | 							HTML body | ||||||
|  | 						</button> | ||||||
|  | 					</li> | ||||||
|  | 					<li v-if="message.Text"> | ||||||
|  | 						<button v-on:click="downloadMessageBody(message.Text, 'txt')" class="dropdown-item"> | ||||||
|  | 							Text body | ||||||
|  | 						</button> | ||||||
|  | 					</li> | ||||||
|  | 					<li v-if="allAttachments(message).length"> | ||||||
|  | 						<hr class="dropdown-divider"> | ||||||
|  | 					</li> | ||||||
|  | 					<li v-for="part in allAttachments(message)"> | ||||||
|  | 						<a :href="'api/v1/message/' + message.ID + '/part/' + part.PartID" type="button" | ||||||
|  | 							class="row m-0 dropdown-item d-flex" target="_blank" | ||||||
|  | 							:title="part.FileName != '' ? part.FileName : '[ unknown ]'" style="min-width: 350px"> | ||||||
|  | 							<div class="col-auto p-0 pe-1"> | ||||||
|  | 								<i class="bi" :class="attachmentIcon(part)"></i> | ||||||
|  | 							</div> | ||||||
|  | 							<div class="col text-truncate p-0 pe-1"> | ||||||
|  | 								{{ part.FileName != '' ? part.FileName : '[ unknown ]' }} | ||||||
|  | 							</div> | ||||||
|  | 							<div class="col-auto text-muted small p-0"> | ||||||
|  | 								{{ getFileSize(part.Size) }} | ||||||
|  | 							</div> | ||||||
|  | 						</a> | ||||||
|  | 					</li> | ||||||
|  | 				</ul> | ||||||
|  | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
|  |  | ||||||
| 		<div class="col col-md-9 col-lg-5" v-if="!message"> | 		<div class="col col-md-9 col-lg-5" v-if="!message"> | ||||||
| @@ -617,6 +664,7 @@ export default { | |||||||
| 				<option value="100">100</option> | 				<option value="100">100</option> | ||||||
| 				<option value="200">200</option> | 				<option value="200">200</option> | ||||||
| 			</select> | 			</select> | ||||||
|  |  | ||||||
| 			<span v-if="searching"> | 			<span v-if="searching"> | ||||||
| 				<b>{{ formatNumber(items.length) }} result<template v-if="items.length != 1">s</template></b> | 				<b>{{ formatNumber(items.length) }} result<template v-if="items.length != 1">s</template></b> | ||||||
| 			</span> | 			</span> | ||||||
| @@ -678,16 +726,16 @@ export default { | |||||||
| 					<button class="list-group-item list-group-item-action" :disabled="!selectedHasUnread()" | 					<button class="list-group-item list-group-item-action" :disabled="!selectedHasUnread()" | ||||||
| 						v-on:click="markSelectedRead"> | 						v-on:click="markSelectedRead"> | ||||||
| 						<i class="bi bi-eye-fill"></i> | 						<i class="bi bi-eye-fill"></i> | ||||||
| 						Mark selected read | 						Mark read | ||||||
| 					</button> | 					</button> | ||||||
| 					<button class="list-group-item list-group-item-action" :disabled="!selectedHasRead()" | 					<button class="list-group-item list-group-item-action" :disabled="!selectedHasRead()" | ||||||
| 						v-on:click="markSelectedUnread"> | 						v-on:click="markSelectedUnread"> | ||||||
| 						<i class="bi bi-eye-slash"></i> | 						<i class="bi bi-eye-slash"></i> | ||||||
| 						Mark selected unread | 						Mark unread | ||||||
| 					</button> | 					</button> | ||||||
| 					<button class="list-group-item list-group-item-action" v-on:click="deleteMessages"> | 					<button class="list-group-item list-group-item-action" v-on:click="deleteMessages"> | ||||||
| 						<i class="bi bi-trash-fill me-1 text-danger"></i> | 						<i class="bi bi-trash-fill me-1 text-danger"></i> | ||||||
| 						Delete selected | 						Delete | ||||||
| 					</button> | 					</button> | ||||||
| 					<button class="list-group-item list-group-item-action" v-on:click="selected = []"> | 					<button class="list-group-item list-group-item-action" v-on:click="selected = []"> | ||||||
| 						<i class="bi bi-x-circle me-1"></i> | 						<i class="bi bi-x-circle me-1"></i> | ||||||
|   | |||||||
| @@ -149,10 +149,6 @@ body.blur { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // .tag.active { |  | ||||||
| //     font-weight: bold; |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| .form-select.tag-selector { | .form-select.tag-selector { | ||||||
|     display: none; |     display: none; | ||||||
| } | } | ||||||
| @@ -166,6 +162,17 @@ body.blur { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #DownloadBtn { | ||||||
|  |     @include media-breakpoint-down(sm) { | ||||||
|  |         position: static; | ||||||
|  |  | ||||||
|  |         .dropdown-menu { | ||||||
|  |             left: 0; | ||||||
|  |             right: 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* PrismJS 1.29.0 - modified! | /* PrismJS 1.29.0 - modified! | ||||||
| https://prismjs.com/download.html#themes=prism-coy&languages=markup+css */ | https://prismjs.com/download.html#themes=prism-coy&languages=markup+css */ | ||||||
| code[class*="language-"], | code[class*="language-"], | ||||||
|   | |||||||
| @@ -211,25 +211,12 @@ export default { | |||||||
| 					</tbody> | 					</tbody> | ||||||
| 				</table> | 				</table> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="col-md-auto text-md-end mt-md-3"> | 			<div class="col-md-auto d-none d-md-block text-end mt-md-3"> | ||||||
| 				<!-- <p class="text-muted small d-none d-md-block mb-2"><small>{{ messageDate(message.Date) }}</small></p> | 				<div class="mt-2 mt-md-0" v-if="allAttachments(message)"> | ||||||
| 																																																																												<p class="text-muted small d-none d-md-block"><small>Size: {{ getFileSize(message.Size) }}</small></p> --> | 					<span class="badge rounded-pill text-bg-secondary p-2"> | ||||||
| 				<div class="dropdown mt-2 mt-md-0" v-if="allAttachments(message)"> |  | ||||||
| 					<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" |  | ||||||
| 						aria-expanded="false"> |  | ||||||
| 						Attachment<span v-if="allAttachments(message).length > 1">s</span> | 						Attachment<span v-if="allAttachments(message).length > 1">s</span> | ||||||
| 						({{ allAttachments(message).length }}) | 						({{ allAttachments(message).length }}) | ||||||
| 					</button> | 					</span> | ||||||
| 					<ul class="dropdown-menu"> |  | ||||||
| 						<li v-for="part in allAttachments(message)"> |  | ||||||
| 							<a :href="'api/v1/message/' + message.ID + '/part/' + part.PartID" type="button" |  | ||||||
| 								class="dropdown-item" target="_blank"> |  | ||||||
| 								<i class="bi" :class="attachmentIcon(part)"></i> |  | ||||||
| 								{{ part.FileName != '' ? part.FileName : '[ unknown ]' }} |  | ||||||
| 								<small class="text-muted ms-2">{{ getFileSize(part.Size) }}</small> |  | ||||||
| 							</a> |  | ||||||
| 						</li> |  | ||||||
| 					</ul> |  | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user