haraka-wildduck
Este commit está contenido en:
134
production/haraka-wildduck/webmail/views/webmail/audit.hbs
Archivo normal
134
production/haraka-wildduck/webmail/views/webmail/audit.hbs
Archivo normal
@@ -0,0 +1,134 @@
|
||||
<h2 class="sub-header" style="display: flex;">
|
||||
<div style="flex-grow: 1">
|
||||
<table class="limited">
|
||||
<tr class="messagerow-{{message.mailbox}}-{{messageData.id}}">
|
||||
<td class="message-subject-line">
|
||||
<span>{{messageData.subject}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="/webmail/{{mailbox.id}}/message/{{messageData.id}}" class="btn btn-default"><span class="glyphicon glyphicon-menu-left" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
Below are displayed timeline events related to the selected message. This includes receive info, forwarding and autoreplies
|
||||
</p>
|
||||
|
||||
{{#each events}}
|
||||
<dl class="dl-horizontal">
|
||||
|
||||
{{#if actionDescription}}
|
||||
{{#if action}}
|
||||
<dt>Action</dt>
|
||||
<dd><strong><span class="text-{{actionLabel}}">{{actionDescription}}</span></strong></dd>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if action}}
|
||||
<dt>Action</dt>
|
||||
<dd><strong>{{action}}</strong></dd>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<dt>ID</dt>
|
||||
<dd><strong>{{id}}{{#if seq}}.{{seq}}{{/if}}</strong></dd>
|
||||
|
||||
<dt>Time</dt>
|
||||
<dd><span class="datestring-fixed" title="{{time}}">{{time}}</span></dd>
|
||||
|
||||
{{#if messageId}}
|
||||
<dt>Message-ID</dt>
|
||||
<dd>{{messageId}}</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if from}}
|
||||
<dt>From</dt>
|
||||
<dd>{{from}}</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if to}}
|
||||
<dt>To</dt>
|
||||
<dd>{{to}}</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if targetList}}
|
||||
<dt>{{#if toTitle}}{{toTitle}}{{else}}Forwarding{{/if}}</dt>
|
||||
<dd>
|
||||
{{#each targetList}}
|
||||
<div><strong>{{../id}}.{{seq}}:</strong> {{text}} <code class="response">{{value}}</code></div>
|
||||
{{/each}}
|
||||
</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if origin}}
|
||||
<dt>Sending host</dt>
|
||||
<dd>{{origin}}</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if src}}
|
||||
<dt>Local address</dt>
|
||||
<dd>{{src}}</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if mx}}
|
||||
<dt>Destination</dt>
|
||||
<dd>{{mx}}
|
||||
{{#if dst}}
|
||||
[{{dst}}]
|
||||
{{/if}}
|
||||
</dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if response}}
|
||||
<dt>Server response</dt>
|
||||
<dd><code class="response">{{response}}</code></dd>
|
||||
{{/if}}
|
||||
|
||||
{{#if error}}
|
||||
<dt>Error message</dt>
|
||||
<dd><code class="response">{{error}}</code></dd>
|
||||
{{/if}}
|
||||
|
||||
</dl>
|
||||
{{/each}}
|
||||
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var stream = new EventSource('/api/events');
|
||||
stream.onmessage = function(e) {
|
||||
var data, row, star, redrawTimer;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (E) {
|
||||
return;
|
||||
}
|
||||
switch (data.command) {
|
||||
case 'COUNTERS': {
|
||||
if (data.mailbox) {
|
||||
if(FAVICON && data.mailbox === INBOX_ID){
|
||||
FAVICON.badge(data.unseen);
|
||||
}
|
||||
|
||||
[].slice.call(document.querySelectorAll('.unseen-counter-' + data.mailbox)).forEach(function(row){
|
||||
if(data.unseen){
|
||||
row.style.display = 'block';
|
||||
row.textContent = data.unseen;
|
||||
}else {
|
||||
row.style.display = 'none';
|
||||
row.textContent = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
46
production/haraka-wildduck/webmail/views/webmail/create.hbs
Archivo normal
46
production/haraka-wildduck/webmail/views/webmail/create.hbs
Archivo normal
@@ -0,0 +1,46 @@
|
||||
|
||||
<h2 class="sub-header"><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> Create folder</h2>
|
||||
|
||||
<form method="post" action="/webmail/create">
|
||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||
|
||||
{{> mailbox}}
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var stream = new EventSource('/api/events');
|
||||
stream.onmessage = function(e) {
|
||||
var data, row, star, redrawTimer;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (E) {
|
||||
return;
|
||||
}
|
||||
switch (data.command) {
|
||||
case 'COUNTERS': {
|
||||
if (data.mailbox) {
|
||||
if(FAVICON && data.mailbox === INBOX_ID){
|
||||
FAVICON.badge(data.unseen);
|
||||
}
|
||||
[].slice.call(document.querySelectorAll('.unseen-counter-' + data.mailbox)).forEach(function(row){
|
||||
if(data.unseen){
|
||||
row.style.display = 'block';
|
||||
row.textContent = data.unseen;
|
||||
}else {
|
||||
row.style.display = 'none';
|
||||
row.textContent = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
754
production/haraka-wildduck/webmail/views/webmail/index.hbs
Archivo normal
754
production/haraka-wildduck/webmail/views/webmail/index.hbs
Archivo normal
@@ -0,0 +1,754 @@
|
||||
|
||||
<h2 class="sub-header">
|
||||
{{#if mailbox.editable}}
|
||||
<div class="pull-right">
|
||||
<a href="/webmail/{{mailbox.id}}/settings" class="btn btn-default"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Settings</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if mailbox.icon}}
|
||||
<span class="glyphicon glyphicon-{{mailbox.icon}}" aria-hidden="true"></span>
|
||||
{{else}}
|
||||
<span class="glyphicon glyphicon-inbox" aria-hidden="true"></span>
|
||||
{{/if}}
|
||||
{{mailbox.name}}
|
||||
</h2>
|
||||
|
||||
<div class="toolbar-container">
|
||||
|
||||
<div class="toolbar-main">
|
||||
<div class="pull-left" style="margin-left: 10px; width: 20px;">
|
||||
<input type="checkbox" class="toggle-all" />
|
||||
</div>
|
||||
<fieldset id="action-toolbar" disabled>
|
||||
<div class="form-group">
|
||||
|
||||
<button class="btn btn-default btn-xs bulk-mark-seen">Mark as Seen</button>
|
||||
<button class="btn btn-default btn-xs bulk-mark-unseen">Mark as Unseen</button>
|
||||
|
||||
<span style="display: inline-block; width: 10px;"></span>
|
||||
|
||||
<button class="btn btn-default btn-xs bulk-delete" data-toggle="modal" data-target="#deleteModal"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete</button>
|
||||
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> Move <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each mailboxes}}
|
||||
{{#if canMoveTo}}
|
||||
<li><a href="#" class="bulk-move" data-mailbox="{{id}}" data-mailbox-path="{{path}}" data-toggle="modal" data-target="#moveModal">
|
||||
{{{prefix}}}
|
||||
{{#if icon}}
|
||||
<span class="glyphicon glyphicon-{{icon}}" aria-hidden="true"></span>
|
||||
{{else}}
|
||||
<span class="glyphicon glyphicon-triangle-right" aria-hidden="true"></span>
|
||||
{{/if}}
|
||||
{{formatted}}
|
||||
{{{suffix}}}</a></li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-search">
|
||||
{{>searchfield}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
{{#if isTrash}}
|
||||
<div class="alert alert-info" style="padding: 5px 15px;" role="alert">Messages in Trash folder are deleted permanently after 30 days</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if isJunk}}
|
||||
<div class="alert alert-info" style="padding: 5px 15px;" role="alert">Messages in Junk Mail folder are deleted permanently after 30 days</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="messagelist">
|
||||
<colgroup>
|
||||
<col class="messagerow-spacer-col" />
|
||||
<col class="messagerow-checkbox-col" />
|
||||
<col class="messagerow-star-col" />
|
||||
<col class="messagerow-from-col" />
|
||||
<col class="messagerow-subject-col" />
|
||||
<col class="messagerow-info-col" />
|
||||
<col class="messagerow-date-col" />
|
||||
<col class="messagerow-spacer-col" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{{#each messages}}
|
||||
{{>messagerow}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav aria-label="nav">
|
||||
<ul class="pager">
|
||||
{{#if previousCursor}}
|
||||
<li class="previous"><a href="/webmail/{{mailbox.id}}?previous={{previousCursor}}&page={{previousPage}}&query={{query}}"><span aria-hidden="true">←</span> Newer</a></li>
|
||||
{{else}}
|
||||
<li class="previous disabled"><a href="#"><span aria-hidden="true">←</span> Newer</a></li>
|
||||
{{/if}}
|
||||
|
||||
<li style="display: inline-block; padding-top: 7px;">
|
||||
Page <strong>{{page}}</strong> (<strong>{{startStr}}</strong>–<strong>{{endStr}}</strong> out of <strong>{{resultsStr}}</strong> messages)
|
||||
</li>
|
||||
|
||||
{{#if nextCursor}}
|
||||
<li class="next"><a href="/webmail/{{mailbox.id}}?next={{nextCursor}}&page={{nextPage}}&query={{query}}">Older <span aria-hidden="true">→</span></a></li>
|
||||
{{else}}
|
||||
<li class="next disabled"><a href="#">Older <span aria-hidden="true">→</span></a></li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="deleteModalLabel">Delete messages</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{#if skipTrash}}
|
||||
Are you sure you want to permanently delete selected messages?
|
||||
{{else}}
|
||||
Are you sure you want to move selected messages to Trash folder?
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="button" class="btn btn-danger bulk-delete-confirm" data-loading-text="Deleting..." >Yes, delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="moveModal" tabindex="-1" role="dialog" aria-labelledby="moveModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="moveModalLabel">Move messages</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to move selected messages to <span class="bulk-move-path">another folder</span>?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="button" class="btn btn-primary bulk-move-confirm" data-loading-text="Moving..." >Yes, move</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script id="messagerow-template" type="text/x-handlebars-template">
|
||||
{{{messageRowTemplate}}}
|
||||
</script>
|
||||
|
||||
<input type="hidden" id="_csrf" value="{{csrfToken}}">
|
||||
<input type="hidden" id="mailbox" value="{{mailbox.id}}">
|
||||
<input type="hidden" id="cursor-type" value="{{cursorType}}">
|
||||
<input type="hidden" id="cursor-value" value="{{cursorValue}}">
|
||||
<input type="hidden" id="page" value="{{page}}">
|
||||
<input type="hidden" id="mailbox-type" value="{{mailbox.specialUse}}">
|
||||
|
||||
<script>
|
||||
|
||||
// star toggle
|
||||
(function(){
|
||||
var toggleStar = function(e, elm){
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if(!elm || elm.dataset.status === 'pending'){
|
||||
return;
|
||||
}
|
||||
|
||||
elm.dataset.status = 'pending';
|
||||
var flagged = elm.classList.contains('flagged');
|
||||
|
||||
var done = function(){
|
||||
elm.dataset.status = 'done';
|
||||
}
|
||||
|
||||
fetch('/api/toggle/flagged', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: elm.dataset.mailbox,
|
||||
message: elm.dataset.message,
|
||||
flagged: !flagged // toggle
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
if(flagged){
|
||||
elm.classList.remove('flagged');
|
||||
elm.classList.add('unflagged');
|
||||
elm.querySelector('.glyphicon').classList.remove('glyphicon-star');
|
||||
elm.querySelector('.glyphicon').classList.add('glyphicon-star-empty');
|
||||
}else{
|
||||
elm.classList.remove('unflagged');
|
||||
elm.classList.add('flagged');
|
||||
elm.querySelector('.glyphicon').classList.remove('glyphicon-star-empty');
|
||||
elm.querySelector('.glyphicon').classList.add('glyphicon-star');
|
||||
}
|
||||
|
||||
done();
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var setupToggling = function(elm){
|
||||
elm.addEventListener('click', function(e){
|
||||
toggleStar(e, elm);
|
||||
}, false);
|
||||
}
|
||||
|
||||
var starElms = document.querySelectorAll('.message-star');
|
||||
for(var i=0, len = starElms.length; i<len; i++){
|
||||
setupToggling(starElms[i]);
|
||||
}
|
||||
})();
|
||||
|
||||
// checkboxes
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var checkboxes = document.querySelectorAll('.message-checkbox');
|
||||
var toolbarElm = document.querySelector('#action-toolbar');
|
||||
var mailboxType = document.querySelector('#mailbox-type');
|
||||
var toggleAllElm = document.querySelector('.toggle-all');
|
||||
|
||||
var isInbox = mailboxType === 'INBOX';
|
||||
var isSent = mailboxType === '\\Sent';
|
||||
var isTrash= mailboxType === '\\Trash';
|
||||
var isJunk = mailboxType === '\\Junk';
|
||||
var skipTrash = ['\\Trash', '\\Junk'].includes(mailboxType);
|
||||
|
||||
var getChecked = function(){
|
||||
var result = [];
|
||||
for(var i=0, len = checkboxes.length; i<len; i++){
|
||||
if(checkboxes[i].checked){
|
||||
result.push({
|
||||
elm: checkboxes[i],
|
||||
message: checkboxes[i].dataset.message,
|
||||
mailbox: checkboxes[i].dataset.mailbox
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var toggleToolbar = function(){
|
||||
var checked = 0;
|
||||
for(var i=0, len = checkboxes.length; i<len; i++){
|
||||
if(checkboxes[i].checked){
|
||||
checked++;
|
||||
}
|
||||
}
|
||||
if(checked){
|
||||
toolbarElm.disabled = false;
|
||||
if(checked === checkboxes.length){
|
||||
toggleAllElm.checked = true;
|
||||
}
|
||||
}else{
|
||||
toolbarElm.disabled = true;
|
||||
toggleAllElm.checked = false;
|
||||
}
|
||||
};
|
||||
|
||||
for(var i=0, len = checkboxes.length; i<len; i++){
|
||||
checkboxes[i].addEventListener('click', toggleToolbar, false);
|
||||
checkboxes[i].addEventListener('change', toggleToolbar, false);
|
||||
}
|
||||
|
||||
var findRow = function(elm, level){
|
||||
level = level || 0;
|
||||
var parent = elm.parentNode;
|
||||
if(!parent || level > 10){
|
||||
return false;
|
||||
}
|
||||
if(parent.classList.contains('messagerow')){
|
||||
return parent;
|
||||
}
|
||||
return findRow(parent, level+1);
|
||||
}
|
||||
|
||||
var removeRow = function(id){
|
||||
var row = document.getElementById('msg_' + id);
|
||||
if(row && row.parentNode){
|
||||
row.parentNode.removeChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
var messagerowSource = document.getElementById('messagerow-template').innerHTML;
|
||||
var messagerowTemplate = Handlebars.compile(messagerowSource);
|
||||
|
||||
// function to redraw email listing
|
||||
var redrawList = function(done){
|
||||
var checkedMessages = {};
|
||||
getChecked().forEach(function(checked){
|
||||
checkedMessages[checked.message] = true;
|
||||
});
|
||||
|
||||
fetch('/api/list', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: document.getElementById('mailbox').value,
|
||||
cursorType: document.getElementById('cursor-type').value,
|
||||
cursorValue: document.getElementById('cursor-value').value,
|
||||
page: document.getElementById('page').value
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
var html = res.results.map(function(message){
|
||||
return messagerowTemplate(message);
|
||||
}).join('\n');
|
||||
|
||||
document.querySelector('.messagelist tbody').innerHTML = html;
|
||||
|
||||
// reset page load handlers
|
||||
checkboxes = document.querySelectorAll('.message-checkbox');
|
||||
for(var i=0, len = checkboxes.length; i<len; i++){
|
||||
if(checkedMessages[checkboxes[i].dataset.message]){
|
||||
checkboxes[i].checked = true;
|
||||
}
|
||||
checkboxes[i].addEventListener('click', toggleToolbar, false);
|
||||
checkboxes[i].addEventListener('change', toggleToolbar, false);
|
||||
}
|
||||
toggleToolbar();
|
||||
$("[rel='tooltip']").tooltip();
|
||||
updateFixedDatestrings()
|
||||
|
||||
done();
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
var pendingSeen = false;
|
||||
var toggleSeen = function(seen){
|
||||
if(pendingSeen){
|
||||
return false;
|
||||
}
|
||||
|
||||
var checked = getChecked();
|
||||
if(!checked.length){
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingSeen = true;
|
||||
|
||||
var groupkeys= [];
|
||||
var groups = {};
|
||||
checked.forEach(function(entry){
|
||||
if(!groups[entry.mailbox]){
|
||||
groups[entry.mailbox] = [];
|
||||
groupkeys.push(entry.mailbox);
|
||||
}
|
||||
groups[entry.mailbox].push(entry.message);
|
||||
})
|
||||
|
||||
var done = function(){
|
||||
pendingSeen = false;
|
||||
}
|
||||
|
||||
var batchPos = 0;
|
||||
var processBatch = function(){
|
||||
if(batchPos >= groupkeys.length){
|
||||
return done();
|
||||
}
|
||||
var mailbox = groupkeys[batchPos++];
|
||||
var messages = groups[mailbox];
|
||||
|
||||
fetch('/api/toggle/seen', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: messages.join(','),
|
||||
seen: !!seen
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
checked.forEach(function(checkbox){
|
||||
var row = findRow(checkbox.elm);
|
||||
if(row){
|
||||
row.classList.remove(seen ? 'message-unseen' : 'message-seen');
|
||||
row.classList.add(seen ? 'message-seen' : 'message-unseen');
|
||||
}
|
||||
})
|
||||
|
||||
// continue processing
|
||||
processBatch();
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
processBatch();
|
||||
}
|
||||
|
||||
var setUnseen = function(){
|
||||
toggleSeen(false);
|
||||
};
|
||||
|
||||
var setSeen = function(){
|
||||
toggleSeen(true);
|
||||
};
|
||||
|
||||
document.querySelector('.bulk-mark-unseen').addEventListener('click', setUnseen, false);
|
||||
document.querySelector('.bulk-mark-unseen').addEventListener('touch', setUnseen, false);
|
||||
document.querySelector('.bulk-mark-seen').addEventListener('click', setSeen, false);
|
||||
document.querySelector('.bulk-mark-seen').addEventListener('touch', setSeen, false);
|
||||
|
||||
var toggleAll = function(){
|
||||
var checked = toggleAllElm.checked;
|
||||
|
||||
for(var i=0, len = checkboxes.length; i<len; i++){
|
||||
checkboxes[i].checked = checked;
|
||||
}
|
||||
|
||||
if(checked && checkboxes.length){
|
||||
toolbarElm.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('.toggle-all').addEventListener('click', toggleAll, false);
|
||||
document.querySelector('.toggle-all').addEventListener('change', toggleAll, false);
|
||||
|
||||
var pendingDeleted = false;
|
||||
var deleteMessage = function(){
|
||||
if(pendingDeleted){
|
||||
return false;
|
||||
}
|
||||
|
||||
var checked = getChecked();
|
||||
if(!checked.length){
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingDeleted = true;
|
||||
$('#deleteModal .bulk-delete-confirm').button('loading');
|
||||
|
||||
var done = function(){
|
||||
pendingDeleted = false;
|
||||
$('#deleteModal .bulk-delete-confirm').button('reset');
|
||||
$('#deleteModal').modal('hide');
|
||||
}
|
||||
|
||||
var groupkeys= [];
|
||||
var groups = {};
|
||||
checked.forEach(function(entry){
|
||||
if(!groups[entry.mailbox]){
|
||||
groups[entry.mailbox] = [];
|
||||
groupkeys.push(entry.mailbox);
|
||||
}
|
||||
groups[entry.mailbox].push(entry.message);
|
||||
})
|
||||
|
||||
var deleted = 0;
|
||||
var batchPos = 0;
|
||||
var processBatch = function(){
|
||||
if(batchPos >= groupkeys.length){
|
||||
if(deleted){
|
||||
return redrawList(done);
|
||||
}
|
||||
return done();
|
||||
}
|
||||
var mailbox = groupkeys[batchPos++];
|
||||
var messages = groups[mailbox];
|
||||
|
||||
fetch('/api/delete', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: messages.join(',')
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
var removeRow = function(id){
|
||||
var row = document.getElementById('msg_' + id);
|
||||
if(row && row.parentNode){
|
||||
row.parentNode.removeChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
if(res.id && res.id.length){
|
||||
for(var i=0, len = res.id.length; i<len; i++){
|
||||
if(res.id[i] && res.id[i][0] && res.id[i][1]){
|
||||
removeRow(res.id[i][0]);
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processBatch();
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
}
|
||||
processBatch();
|
||||
};
|
||||
|
||||
document.querySelector('.bulk-delete-confirm').addEventListener('click', deleteMessage, false);
|
||||
document.querySelector('.bulk-delete-confirm').addEventListener('touch', deleteMessage, false);
|
||||
|
||||
var pendingMove = false;
|
||||
var moveMessage = function(target){
|
||||
if(pendingMove){
|
||||
return false;
|
||||
}
|
||||
|
||||
var checked = getChecked();
|
||||
if(!checked.length){
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingMove = true;
|
||||
$('#moveModal .bulk-move-confirm').button('loading');
|
||||
|
||||
var done = function(){
|
||||
pendingMove = false;
|
||||
$('#moveModal .bulk-move-confirm').button('reset');
|
||||
$('#moveModal').modal('hide');
|
||||
}
|
||||
|
||||
var targetMailbox = document.querySelector('.bulk-move-confirm').dataset.mailbox;
|
||||
var groupkeys= [];
|
||||
var groups = {};
|
||||
checked.forEach(function(entry){
|
||||
if(targetMailbox === entry.mailbox){
|
||||
// skip
|
||||
return;
|
||||
}
|
||||
if(!groups[entry.mailbox]){
|
||||
groups[entry.mailbox] = [];
|
||||
groupkeys.push(entry.mailbox);
|
||||
}
|
||||
groups[entry.mailbox].push(entry.message);
|
||||
})
|
||||
|
||||
var moved = 0;
|
||||
var batchPos = 0;
|
||||
var processBatch = function(){
|
||||
if(batchPos >= groupkeys.length){
|
||||
if(moved){
|
||||
return redrawList(done);
|
||||
}
|
||||
return done();
|
||||
}
|
||||
var mailbox = groupkeys[batchPos++];
|
||||
var messages = groups[mailbox];
|
||||
|
||||
fetch('/api/move', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: messages.join(','),
|
||||
target: targetMailbox
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
if(res.id && res.id.length){
|
||||
for(var i=0, len = res.id.length; i<len; i++){
|
||||
if(res.id[i] && res.id[i][0] && res.id[i][1]){
|
||||
removeRow(res.id[i][0]);
|
||||
moved++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processBatch();
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
}
|
||||
processBatch();
|
||||
};
|
||||
|
||||
$('#moveModal').on('show.bs.modal', function (event) {
|
||||
var button = $(event.relatedTarget); // Button that triggered the modal
|
||||
var mailbox = button.data('mailbox'); // Extract info from data-* attributes
|
||||
var path = button.data('mailbox-path');
|
||||
$('.bulk-move-path').text(path);
|
||||
document.querySelector('.bulk-move-confirm').dataset.mailbox = mailbox;
|
||||
});
|
||||
|
||||
document.querySelector('.bulk-move-confirm').addEventListener('click', moveMessage, false);
|
||||
document.querySelector('.bulk-move-confirm').addEventListener('touch', moveMessage, false);
|
||||
|
||||
let checkNewMessages = document.getElementById('page').value === '1' && document.getElementById('mailbox').value;
|
||||
|
||||
var stream = new EventSource('/api/events');
|
||||
stream.onmessage = function(e) {
|
||||
var data, row, star, redrawTimer;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (E) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.command) {
|
||||
case 'EXISTS':
|
||||
if(checkNewMessages === data.mailbox){
|
||||
clearTimeout(redrawTimer);
|
||||
redrawTimer = setTimeout(function(){
|
||||
clearTimeout(redrawTimer);
|
||||
redrawList(function(){});
|
||||
}, 200);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'FETCH': {
|
||||
row = document.querySelector('.messagerow-'+data.mailbox+'-'+data.uid);
|
||||
if(!row){
|
||||
return;
|
||||
}
|
||||
if (data.flags) {
|
||||
star = row.querySelector('.message-star');
|
||||
|
||||
if (data.flags.indexOf('\\Flagged')>=0) {
|
||||
star.classList.remove('unflagged');
|
||||
star.classList.add('flagged');
|
||||
star.querySelector('.glyphicon').classList.remove('glyphicon-star-empty');
|
||||
star.querySelector('.glyphicon').classList.add('glyphicon-star');
|
||||
} else {
|
||||
star.classList.remove('flagged');
|
||||
star.classList.add('unflagged');
|
||||
star.querySelector('.glyphicon').classList.remove('glyphicon-star');
|
||||
star.querySelector('.glyphicon').classList.add('glyphicon-star-empty');
|
||||
}
|
||||
|
||||
if (data.flags.indexOf('\\Seen')>=0) {
|
||||
row.classList.remove('message-unseen');
|
||||
row.classList.add('message-seen');
|
||||
} else {
|
||||
row.classList.remove('message-seen');
|
||||
row.classList.add('message-unseen');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'EXPUNGE': {
|
||||
row = document.querySelector('.messagerow-'+data.mailbox+'-'+data.uid);
|
||||
if(!row){
|
||||
return;
|
||||
}
|
||||
if(row.parentNode){
|
||||
row.parentNode.removeChild(row);
|
||||
clearTimeout(redrawTimer);
|
||||
redrawTimer = setTimeout(function(){
|
||||
clearTimeout(redrawTimer);
|
||||
redrawList(function(){});
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
case 'COUNTERS': {
|
||||
if (data.mailbox) {
|
||||
if(FAVICON && data.mailbox === INBOX_ID){
|
||||
FAVICON.badge(data.unseen);
|
||||
}
|
||||
[].slice.call(document.querySelectorAll('.unseen-counter-' + data.mailbox)).forEach(function(row){
|
||||
if(data.unseen){
|
||||
row.style.display = 'block';
|
||||
row.textContent = data.unseen;
|
||||
}else {
|
||||
row.style.display = 'none';
|
||||
row.textContent = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
toggleToolbar();
|
||||
}, false);
|
||||
|
||||
</script>
|
||||
75
production/haraka-wildduck/webmail/views/webmail/mailbox.hbs
Archivo normal
75
production/haraka-wildduck/webmail/views/webmail/mailbox.hbs
Archivo normal
@@ -0,0 +1,75 @@
|
||||
|
||||
<h2 class="sub-header"><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> {{mailbox.name}}</h2>
|
||||
|
||||
<form method="post" action="/webmail/{{mailbox.id}}/settings">
|
||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||
|
||||
{{> mailbox}}
|
||||
|
||||
{{#unless isInbox}}
|
||||
<div class="form-group">
|
||||
{{#unless isSpecial}}
|
||||
<div class="pull-right">
|
||||
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete</button>
|
||||
</div>
|
||||
{{/unless}}
|
||||
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> Update</button>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
</form>
|
||||
|
||||
<div class="modal" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="deleteModalLabel">Delete folder</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to permanently delete <strong>{{mailbox.name}}</strong> and all its contents?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<form method="post" action="/webmail/{{mailbox.id}}/delete">
|
||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Yes, delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var stream = new EventSource('/api/events');
|
||||
stream.onmessage = function(e) {
|
||||
var data, row, star, redrawTimer;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (E) {
|
||||
return;
|
||||
}
|
||||
switch (data.command) {
|
||||
case 'COUNTERS': {
|
||||
if (data.mailbox) {
|
||||
if(FAVICON && data.mailbox === INBOX_ID){
|
||||
FAVICON.badge(data.unseen);
|
||||
}
|
||||
[].slice.call(document.querySelectorAll('.unseen-counter-' + data.mailbox)).forEach(function(row){
|
||||
if(data.unseen){
|
||||
row.style.display = 'block';
|
||||
row.textContent = data.unseen;
|
||||
}else {
|
||||
row.style.display = 'none';
|
||||
row.textContent = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
630
production/haraka-wildduck/webmail/views/webmail/message.hbs
Archivo normal
630
production/haraka-wildduck/webmail/views/webmail/message.hbs
Archivo normal
@@ -0,0 +1,630 @@
|
||||
<input type="hidden" id="mailbox" value="{{mailbox.id}}" />
|
||||
<input type="hidden" id="message" value="{{message.id}}" />
|
||||
<input type="hidden" id="_csrf" value="{{csrfToken}}">
|
||||
|
||||
<h2 class="sub-header" style="display: flex;">
|
||||
<div style="flex-grow: 1">
|
||||
<table class="limited">
|
||||
<tr class="messagerow-{{message.mailbox}}-{{message.id}}">
|
||||
<td class="message-subject-line">
|
||||
<a href="#" class="message-star {{#if message.flagged}}flagged{{else}}unflagged{{/if}}"
|
||||
data-mailbox="{{mailbox.id}}" data-message="{{message.id}}"><span
|
||||
class="glyphicon glyphicon-{{#if message.flagged}}star{{else}}star-empty{{/if}}"
|
||||
aria-hidden="true"></span></a>
|
||||
|
||||
<span>{{message.subject}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-envelope" aria-hidden="true"></span> Details <span
|
||||
class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a href="/webmail/{{mailbox.id}}/raw/{{message.id}}.eml"><span
|
||||
class="glyphicon glyphicon-download-alt" aria-hidden="true"></span> Original message</a>
|
||||
</li>
|
||||
{{#if message.attachments}}
|
||||
<li role="separator" class="divider"></li>
|
||||
{{#each message.attachments}}
|
||||
<li><a href="/webmail/{{../mailbox.id}}/attachment/{{../message.id}}/{{id}}"
|
||||
download="{{filename}}"><span class="glyphicon glyphicon-paperclip" aria-hidden="true"></span>
|
||||
{{filename}} [{{sizeKb}}kB]</a></li>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<div class="toolbar-container">
|
||||
<div class="toolbar-main">
|
||||
|
||||
<fieldset id="action-toolbar">
|
||||
<div class="form-group">
|
||||
|
||||
<a href="/webmail/send?action=reply&refMailbox={{mailbox.id}}&refMessage={{message.id}}"
|
||||
class="btn btn-default btn-xs"><span class="glyphicon glyphicon-send" aria-hidden="true"></span>
|
||||
Reply</a>
|
||||
<a href="/webmail/send?action=replyAll&refMailbox={{mailbox.id}}&refMessage={{message.id}}"
|
||||
class="btn btn-default btn-xs"><span class="glyphicon glyphicon-send" aria-hidden="true"></span>
|
||||
Reply to all</a>
|
||||
<a href="/webmail/send?action=forward&refMailbox={{mailbox.id}}&refMessage={{message.id}}"
|
||||
class="btn btn-default btn-xs"><span class="glyphicon glyphicon-share" aria-hidden="true"></span>
|
||||
Forward</a>
|
||||
|
||||
<span style="display: inline-block; width: 10px;"></span>
|
||||
|
||||
<button class="btn btn-default btn-xs bulk-mark-unseen">Mark as Unseen</button>
|
||||
|
||||
<span style="display: inline-block; width: 10px;"></span>
|
||||
|
||||
<button class="btn btn-default btn-xs bulk-delete" data-toggle="modal" data-target="#deleteModal"><span
|
||||
class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> Move <span
|
||||
class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each mailboxes}}
|
||||
{{#if canMoveTo}}
|
||||
<li><a href="#" class="bulk-move" data-mailbox="{{id}}" data-mailbox-path="{{path}}"
|
||||
data-toggle="modal" data-target="#moveModal">
|
||||
{{{prefix}}}
|
||||
{{#if icon}}
|
||||
<span class="glyphicon glyphicon-{{icon}}" aria-hidden="true"></span>
|
||||
{{else}}
|
||||
<span class="glyphicon glyphicon-triangle-right" aria-hidden="true"></span>
|
||||
{{/if}}
|
||||
{{formatted}}
|
||||
{{{suffix}}}</a></li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-search">
|
||||
{{>searchfield}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
{{#each message.info}}
|
||||
<div>
|
||||
<strong>{{key}}:</strong>
|
||||
{{#if icon}}
|
||||
<span class="glyphicon glyphicon-{{icon}}" aria-hidden="true"></span>
|
||||
{{/if}}
|
||||
<span {{#if isDate}} class="datestring" title="{{value}}" {{/if}}>
|
||||
{{#if isHtml}}{{{value}}}{{else}}{{value}}{{/if}}
|
||||
</span>
|
||||
|
||||
{{#if @first}}
|
||||
{{#if ../message.securityInfo}}
|
||||
<a id="extraDetails" tabindex="0" role="button" data-toggle="popover" data-trigger="focus"
|
||||
title="Delivery details"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></a>
|
||||
<div id="extraDetailsContent" style="display: none">
|
||||
{{#each ../message.securityInfo}}
|
||||
<div {{#if textClass}} class="{{textClass}}" {{/if}}>
|
||||
<strong>{{key}}:</strong>
|
||||
{{#if icon}}
|
||||
<span class="glyphicon glyphicon-{{icon}}" aria-hidden="true"></span>
|
||||
{{/if}}
|
||||
<span {{#if isDate}} class="datestring" title="{{value}}" {{/if}}>
|
||||
{{#if isHtml}}{{{value}}}{{else}}{{value}}{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#if expires}}
|
||||
<div class="text-muted">
|
||||
<strong>Message expires:</strong>
|
||||
<span class="datestring" title="{{expires}}">
|
||||
{{expires}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div style="margin-bottom: 5px;"></div>
|
||||
|
||||
{{#if message.encrypted}}
|
||||
<div id="encrypted-warning" class="alert alert-warning" role="alert">
|
||||
<span class="glyphicon glyphicon-lock" aria-hidden="true"></span>
|
||||
This message is encrypted and can not be displayed. Download the original message <a
|
||||
href="/webmail/{{mailbox.id}}/raw/{{message.id}}.eml" download="{{message.id}}.eml" class="alert-link">from here
|
||||
</a> to open it in an e-mail client that is able to read encrypted messages.
|
||||
Alternatively you can install <a href="https://www.mailvelope.com/" class="alert-link">Mailvelope browser
|
||||
extension</a> to allow decrypting and displaying messages by {{serviceName}}.
|
||||
</div>
|
||||
|
||||
<div id="mailvelope-loading" class="alert alert-info" role="alert" style="display:none">
|
||||
<span class="glyphicon glyphicon-lock" aria-hidden="true"></span>
|
||||
Mailvelope detected. Trying to decrpyt encrypted message...
|
||||
</div>
|
||||
|
||||
<div id="message-content" class="mailvelope"></div>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
if (typeof mailvelope !== 'undefined') {
|
||||
mailvelopeLoaded();
|
||||
} else {
|
||||
window.addEventListener('mailvelope', mailvelopeLoaded, false);
|
||||
}
|
||||
|
||||
var identifier = 'wildduck';
|
||||
|
||||
function getKeyring(callback) {
|
||||
mailvelope.getKeyring(identifier).then(function (keyring) {
|
||||
return callback(null, keyring);
|
||||
}).catch(function (err) {
|
||||
mailvelope.createKeyring(identifier).then(function (keyring) {
|
||||
return callback(null, keyring);
|
||||
}).catch(function (err) {
|
||||
return callback(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mailvelopeLoaded() {
|
||||
|
||||
document.getElementById('encrypted-warning').style.display = 'none';
|
||||
document.getElementById('mailvelope-loading').style.display = 'block';
|
||||
|
||||
var selector = '#message-content';
|
||||
|
||||
getKeyring(function (err, keyring) {
|
||||
if (err) {
|
||||
document.getElementById('encrypted-warning').style.display = 'block';
|
||||
document.getElementById('mailvelope-loading').style.display = 'none';
|
||||
alert('Failed to create keyring. ' + err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/webmail/{{mailbox.id}}/raw/{{message.id}}.eml', {
|
||||
method: 'get',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(function (res) {
|
||||
return res.text();
|
||||
})
|
||||
.then(function (text) {
|
||||
mailvelope.createDisplayContainer(selector, text, keyring, { showExternalContent: true }).then(function () {
|
||||
//$(selector).addClass('mailvelope').find('.message-part, .part-notice').hide();
|
||||
//setTimeout(function() { $(window).resize(); }, 10);
|
||||
document.getElementById('mailvelope-loading').style.display = 'none';
|
||||
}).catch(function (err) {
|
||||
document.getElementById('encrypted-warning').style.display = 'block';
|
||||
document.getElementById('mailvelope-loading').style.display = 'none';
|
||||
console.error(err);
|
||||
alert('Message decryption failed: ' + err.message, 'error')
|
||||
});
|
||||
}).catch(function (err) {
|
||||
document.getElementById('encrypted-warning').style.display = 'block';
|
||||
document.getElementById('mailvelope-loading').style.display = 'none';
|
||||
console.error(err);
|
||||
alert('Message decryption failed: ' + err.message)
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
{{else}}
|
||||
|
||||
<div id="message-content" class="iframe-box"></div>
|
||||
|
||||
{{#if message.attachments}}
|
||||
<div class="well">
|
||||
{{#each message.attachments}}
|
||||
<a class="btn btn-success btn-sm" href="/webmail/{{../mailbox.id}}/attachment/{{../message.id}}/{{id}}"
|
||||
role="button" download="{{filename}}"><span class="glyphicon glyphicon-cloud-download"
|
||||
aria-hidden="true"></span> {{filename}}</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<script type="text/javascript" src="/components/DOMPurify/dist/purify.min.js"></script>
|
||||
<script>
|
||||
var message = {{{ messageJson }}};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (event) {
|
||||
if (message.html) {
|
||||
var clean = DOMPurify.sanitize(message.html.join('\n'), {
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
WHOLE_DOCUMENT: true,
|
||||
FORBID_TAGS: ['form']
|
||||
});
|
||||
|
||||
clean = clean.replace(/head>/, 'head><link rel="stylesheet" href="/css/mail.css" /><base target="_parent"><script>function resizeIframe(obj) {obj.style.height = obj.contentWindow.document.body.scrollHeight + "px";}</' + 'script>');
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
|
||||
document.getElementById('message-content').appendChild(iframe);
|
||||
iframe.contentWindow.document.open();
|
||||
iframe.contentWindow.document.write(clean);
|
||||
iframe.contentWindow.document.close();
|
||||
iframe.contentWindow.addEventListener('load', function () {
|
||||
iframe.contentWindow.resizeIframe(iframe);
|
||||
});
|
||||
iframe.contentWindow.document.addEventListener('DOMContentLoaded', function () {
|
||||
iframe.contentWindow.resizeIframe(iframe);
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
</script>
|
||||
|
||||
{{/if}}
|
||||
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="deleteModalLabel">Delete message</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{#if isTrash}}
|
||||
Are you sure you want to permanently delete this message?
|
||||
{{else}}
|
||||
Are you sure you want to move this message to Trash folder?
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="button" class="btn btn-danger bulk-delete-confirm" data-loading-text="Deleting...">Yes,
|
||||
delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="moveModal" tabindex="-1" role="dialog" aria-labelledby="moveModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="moveModalLabel">Move message</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to move this message to <span class="bulk-move-path">another folder</span>?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="button" class="btn btn-primary bulk-move-confirm" data-loading-text="Moving...">Yes,
|
||||
move</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// star toggle
|
||||
(function () {
|
||||
var toggleStar = function (e, elm) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (!elm || elm.dataset.status === 'pending') {
|
||||
return;
|
||||
}
|
||||
|
||||
elm.dataset.status = 'pending';
|
||||
var flagged = elm.classList.contains('flagged');
|
||||
|
||||
var done = function () {
|
||||
elm.dataset.status = 'done';
|
||||
}
|
||||
|
||||
fetch('/api/toggle/flagged', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: elm.dataset.mailbox,
|
||||
message: elm.dataset.message,
|
||||
flagged: !flagged // toggle
|
||||
})
|
||||
})
|
||||
.then(function (res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.error) {
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
if (flagged) {
|
||||
elm.classList.remove('flagged');
|
||||
elm.classList.add('unflagged');
|
||||
elm.querySelector('.glyphicon').classList.remove('glyphicon-star');
|
||||
elm.querySelector('.glyphicon').classList.add('glyphicon-star-empty');
|
||||
} else {
|
||||
elm.classList.remove('unflagged');
|
||||
elm.classList.add('flagged');
|
||||
elm.querySelector('.glyphicon').classList.remove('glyphicon-star-empty');
|
||||
elm.querySelector('.glyphicon').classList.add('glyphicon-star');
|
||||
}
|
||||
|
||||
done();
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var setupToggling = function (elm) {
|
||||
elm.addEventListener('click', function (e) {
|
||||
toggleStar(e, elm);
|
||||
}, false);
|
||||
}
|
||||
|
||||
var starElms = document.querySelectorAll('.message-star');
|
||||
for (var i = 0, len = starElms.length; i < len; i++) {
|
||||
setupToggling(starElms[i]);
|
||||
}
|
||||
})();
|
||||
|
||||
// checkboxes
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var toolbarElm = document.querySelector('#action-toolbar');
|
||||
var mailbox = document.getElementById('mailbox').value;
|
||||
var message = document.getElementById('message').value;
|
||||
|
||||
var pendingSeen = false;
|
||||
var toggleSeen = function (seen) {
|
||||
if (pendingSeen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var done = function () {
|
||||
pendingSeen = false;
|
||||
}
|
||||
|
||||
fetch('/api/toggle/seen', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: message,
|
||||
seen: !!seen
|
||||
})
|
||||
})
|
||||
.then(function (res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.error) {
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
window.location.href = '/webmail/' + mailbox;
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
var setUnseen = function () {
|
||||
toggleSeen(false);
|
||||
};
|
||||
|
||||
var setSeen = function () {
|
||||
toggleSeen(true);
|
||||
};
|
||||
|
||||
document.querySelector('.bulk-mark-unseen').addEventListener('click', setUnseen, false);
|
||||
document.querySelector('.bulk-mark-unseen').addEventListener('touch', setUnseen, false);
|
||||
|
||||
var pendingDeleted = false;
|
||||
var deleteMessage = function () {
|
||||
if (pendingDeleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingDeleted = true;
|
||||
$('#deleteModal .bulk-delete-confirm').button('loading');
|
||||
|
||||
var done = function () {
|
||||
pendingDeleted = false;
|
||||
$('#deleteModal .bulk-delete-confirm').button('reset');
|
||||
$('#deleteModal').modal('hide');
|
||||
}
|
||||
|
||||
fetch('/api/delete', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: message
|
||||
})
|
||||
})
|
||||
.then(function (res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.error) {
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
window.location.href = '/webmail/' + mailbox;
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
document.querySelector('.bulk-delete-confirm').addEventListener('click', deleteMessage, false);
|
||||
document.querySelector('.bulk-delete-confirm').addEventListener('touch', deleteMessage, false);
|
||||
|
||||
var pendingMove = false;
|
||||
var moveMessage = function (target) {
|
||||
if (pendingMove) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingMove = true;
|
||||
$('#moveModal .bulk-move-confirm').button('loading');
|
||||
|
||||
var done = function () {
|
||||
pendingMove = false;
|
||||
$('#moveModal .bulk-move-confirm').button('reset');
|
||||
$('#moveModal').modal('hide');
|
||||
}
|
||||
|
||||
var targetMailbox = document.querySelector('.bulk-move-confirm').dataset.mailbox;
|
||||
|
||||
fetch('/api/move', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: message,
|
||||
target: targetMailbox
|
||||
})
|
||||
})
|
||||
.then(function (res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.error) {
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
window.location.href = '/webmail/' + targetMailbox;
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
$('#moveModal').on('show.bs.modal', function (event) {
|
||||
var button = $(event.relatedTarget); // Button that triggered the modal
|
||||
var mailbox = button.data('mailbox'); // Extract info from data-* attributes
|
||||
var path = button.data('mailbox-path');
|
||||
$('.bulk-move-path').text(path);
|
||||
document.querySelector('.bulk-move-confirm').dataset.mailbox = mailbox;
|
||||
});
|
||||
|
||||
document.querySelector('.bulk-move-confirm').addEventListener('click', moveMessage, false);
|
||||
document.querySelector('.bulk-move-confirm').addEventListener('touch', moveMessage, false);
|
||||
|
||||
var stream = new EventSource('/api/events');
|
||||
stream.onmessage = function (e) {
|
||||
var data, row, star, redrawTimer;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (E) {
|
||||
return;
|
||||
}
|
||||
switch (data.command) {
|
||||
case 'FETCH': {
|
||||
row = document.querySelector('.messagerow-' + data.mailbox + '-' + data.uid);
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
if (data.flags) {
|
||||
star = row.querySelector('.message-star');
|
||||
|
||||
if (data.flags.indexOf('\\Flagged') >= 0) {
|
||||
star.classList.remove('unflagged');
|
||||
star.classList.add('flagged');
|
||||
star.querySelector('.glyphicon').classList.remove('glyphicon-star-empty');
|
||||
star.querySelector('.glyphicon').classList.add('glyphicon-star');
|
||||
} else {
|
||||
star.classList.remove('flagged');
|
||||
star.classList.add('unflagged');
|
||||
star.querySelector('.glyphicon').classList.remove('glyphicon-star');
|
||||
star.querySelector('.glyphicon').classList.add('glyphicon-star-empty');
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'COUNTERS': {
|
||||
if (data.mailbox) {
|
||||
if (FAVICON && data.mailbox === INBOX_ID) {
|
||||
FAVICON.badge(data.unseen);
|
||||
}
|
||||
[].slice.call(document.querySelectorAll('.unseen-counter-' + data.mailbox)).forEach(function (row) {
|
||||
if (data.unseen) {
|
||||
row.style.display = 'block';
|
||||
row.textContent = data.unseen;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
row.textContent = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$('#extraDetails').popover({
|
||||
content: document.getElementById('extraDetailsContent').innerHTML,
|
||||
html: true
|
||||
})
|
||||
}, false);
|
||||
|
||||
</script>
|
||||
382
production/haraka-wildduck/webmail/views/webmail/send.hbs
Archivo normal
382
production/haraka-wildduck/webmail/views/webmail/send.hbs
Archivo normal
@@ -0,0 +1,382 @@
|
||||
|
||||
|
||||
<form method="post" id="send-form" class="form-horizontal" action="/webmail/send" enctype="multipart/form-data">
|
||||
<input type="hidden" id="_csrf" name="_csrf" value="{{csrfToken}}">
|
||||
<input type="hidden" name="action" value="{{values.action}}">
|
||||
<input type="hidden" name="refMailbox" value="{{values.refMailbox}}">
|
||||
<input type="hidden" name="refMessage" value="{{values.refMessage}}">
|
||||
<input type="hidden" id="mailbox" name="draftMailbox" value="{{values.draftMailbox}}">
|
||||
<input type="hidden" id="message" name="draftMessage" value="{{values.draftMessage}}">
|
||||
<input type="hidden" name="draft" value="{{values.draft}}">
|
||||
|
||||
<div class="toolbar-container">
|
||||
<div class="toolbar-main">
|
||||
<fieldset id="action-toolbar">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-11" style="margin-top: 20px;">
|
||||
<button class="btn btn-primary btn-xs" type="button" data-toggle="modal" data-target="#sendModal"><span class="glyphicon glyphicon-send" aria-hidden="true"></span> Send message</button>
|
||||
|
||||
<button class="btn btn-default btn-xs" type="submit" name="userAction" value="save"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> Save draft</button>
|
||||
|
||||
{{#if values.draft}}
|
||||
<button class="btn btn-default btn-xs" type="button" data-toggle="modal" data-target="#deleteModal"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Discard Draft</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="from-field" class="form-group{{#if errors.from}} has-error{{/if}}" {{#unless fromAddress}}style="display:none"{{/unless}}>
|
||||
<label for="inputFrom" class="col-sm-1 control-label">From</label>
|
||||
<div class="col-sm-11">
|
||||
<select class="form-control" name="from" id="inputFrom">
|
||||
{{#each addresses}}
|
||||
<option value="{{id}}" {{#if selected}}selected{{/if}}>
|
||||
{{#if name}}{{name}} – {{/if}} {{address}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{#if errors.from}}
|
||||
<span class="help-block">{{errors.from}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group{{#if errors.to}} has-error{{/if}}">
|
||||
<label for="inputTo" class="col-sm-1 control-label">To</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" name="to" id="inputTo" value="{{values.to}}" placeholder="Recipient">
|
||||
{{#if errors.to}}
|
||||
<span class="help-block">{{errors.to}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cc-field" class="form-group{{#if errors.cc}} has-error{{/if}}" {{#unless values.cc}}style="display:none"{{/unless}}>
|
||||
<label for="inputCc" class="col-sm-1 control-label">Cc</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" name="cc" id="inputCc" value="{{values.cc}}" placeholder="Cc">
|
||||
{{#if errors.cc}}
|
||||
<span class="help-block">{{errors.cc}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bcc-field" class="form-group{{#if errors.bcc}} has-error{{/if}}" {{#unless values.bcc}}style="display:none"{{/unless}}>
|
||||
<label for="inputBcc" class="col-sm-1 control-label">Bcc</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" name="bcc" id="inputBcc" value="{{values.bcc}}" placeholder="Bcc">
|
||||
{{#if errors.bcc}}
|
||||
<span class="help-block">{{errors.bcc}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right" style="margin-top: -10px; margin-bottom: 10px;">
|
||||
<a href="#" id="link-add-from" {{#if fromAddress}}style="display:none"{{/if}}>From</a>
|
||||
<a href="#" id="link-add-cc" {{#if values.cc}}style="display:none"{{/if}}>Cc</a>
|
||||
<a href="#" id="link-add-bcc" {{#if values.bcc}}style="display:none"{{/if}}>Bcc</a>
|
||||
</div>
|
||||
|
||||
<div class="form-group{{#if errors.subject}} has-error{{/if}}">
|
||||
<label for="inputSubject" class="col-sm-1 control-label">Subject</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" id="inputSubject" name="subject" value="{{values.subject}}" placeholder="Message subject">
|
||||
{{#if errors.subject}}
|
||||
<span class="help-block">{{errors.subject}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group{{#if errors.editordata}} has-error{{/if}}">
|
||||
<div class="col-sm-12">
|
||||
<textarea id="summernote" name="editordata"></textarea>
|
||||
{{#if errors.editordata}}
|
||||
<span class="help-block">{{errors.editordata}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="attachment-field" class="form-group{{#if errors.attachment}} has-error{{/if}}">
|
||||
<div class="col-sm-12">
|
||||
|
||||
|
||||
<label for="inputAttachment">Attachments</label>
|
||||
<input id="input-attachment" name="attachment" class="form-control file" type="file" multiple>
|
||||
{{#if errors.attachment}}
|
||||
<span class="help-block">{{errors.attachment}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="deleteModalLabel">Delete draft</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to permanently delete this draft?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="button" class="btn btn-danger bulk-delete-confirm" data-loading-text="Deleting..." >Yes, delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="sendModal" tabindex="-1" role="dialog" aria-labelledby="sendModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="sendModalLabel">Send message</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to send this message?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">No, cancel</button>
|
||||
<button type="submit" name="userAction" value="send" class="btn btn-primary bulk-send-confirm" data-loading-text="Sending..." >Yes, send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="/components/DOMPurify/dist/purify.min.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var messageHtml = {{{messageHtml}}};
|
||||
|
||||
if (messageHtml && messageHtml.length && /^\s*$/.test(document.getElementById('summernote').value)) {
|
||||
// make sure that server timestamps get converted to browser time strings
|
||||
// {%DATE ... %}
|
||||
messageHtml = messageHtml.map(function(html){
|
||||
return html.replace(/\{&DATE ([^&]+)&\}/g, function(m, d){
|
||||
return moment(d.trim()).format('LLLL');
|
||||
});
|
||||
});
|
||||
|
||||
var clean = DOMPurify.sanitize(messageHtml.join('\n'), {
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
WHOLE_DOCUMENT: false,
|
||||
FORBID_TAGS: ['form', 'style']
|
||||
});
|
||||
|
||||
{{#unless keepHtmlAsIs}}
|
||||
clean = '<br/><br/>\n<blockquote>' + clean + '</blockquote>';
|
||||
{{/unless}}
|
||||
|
||||
document.getElementById('summernote').value = clean;
|
||||
}
|
||||
|
||||
$('#summernote').summernote({
|
||||
toolbar: [
|
||||
// [groupName, [list of button]]
|
||||
['style', ['bold', 'italic', 'underline', 'clear']],
|
||||
['fontsize', ['fontsize']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']]
|
||||
],
|
||||
height: 300
|
||||
});
|
||||
|
||||
var linkAddFrom = document.getElementById('link-add-from');
|
||||
var linkAddCc = document.getElementById('link-add-cc');
|
||||
var linkAddBcc = document.getElementById('link-add-bcc');
|
||||
|
||||
var showFrom = function(){
|
||||
document.getElementById('from-field').style.display = 'block';
|
||||
linkAddFrom.style.display = 'none';
|
||||
document.getElementById('inputFrom').focus();
|
||||
};
|
||||
|
||||
var showCc = function(){
|
||||
document.getElementById('cc-field').style.display = 'block';
|
||||
linkAddCc.style.display = 'none';
|
||||
document.getElementById('inputCc').focus();
|
||||
};
|
||||
|
||||
var showBcc = function(){
|
||||
document.getElementById('bcc-field').style.display = 'block';
|
||||
linkAddBcc.style.display = 'none';
|
||||
document.getElementById('inputBcc').focus();
|
||||
};
|
||||
|
||||
linkAddFrom.addEventListener('click', showFrom, false);
|
||||
linkAddFrom.addEventListener('touch', showFrom, false);
|
||||
linkAddCc.addEventListener('click', showCc, false);
|
||||
linkAddCc.addEventListener('touch', showCc, false);
|
||||
linkAddBcc.addEventListener('click', showBcc, false);
|
||||
linkAddBcc.addEventListener('touch', showBcc, false);
|
||||
|
||||
}, false);
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var stream = new EventSource('/api/events');
|
||||
stream.onmessage = function(e) {
|
||||
var data, row, star, redrawTimer;
|
||||
try {
|
||||
data = JSON.parse(e.data);
|
||||
} catch (E) {
|
||||
return;
|
||||
}
|
||||
switch (data.command) {
|
||||
case 'COUNTERS': {
|
||||
if (data.mailbox) {
|
||||
if(FAVICON && data.mailbox === INBOX_ID){
|
||||
FAVICON.badge(data.unseen);
|
||||
}
|
||||
[].slice.call(document.querySelectorAll('.unseen-counter-' + data.mailbox)).forEach(function(row){
|
||||
if(data.unseen){
|
||||
row.style.display = 'block';
|
||||
row.textContent = data.unseen;
|
||||
}else {
|
||||
row.style.display = 'none';
|
||||
row.textContent = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// star toggle
|
||||
(function(){
|
||||
var toggleStar = function(e, elm){
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if(!elm || elm.dataset.status === 'pending'){
|
||||
return;
|
||||
}
|
||||
|
||||
elm.dataset.status = 'pending';
|
||||
var flagged = elm.classList.contains('flagged');
|
||||
|
||||
var done = function(){
|
||||
elm.dataset.status = 'done';
|
||||
}
|
||||
|
||||
fetch('/api/toggle/flagged', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: elm.dataset.mailbox,
|
||||
message: elm.dataset.message,
|
||||
flagged: !flagged // toggle
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
if(flagged){
|
||||
elm.classList.remove('flagged');
|
||||
elm.classList.add('unflagged');
|
||||
elm.querySelector('.glyphicon').classList.remove('glyphicon-star');
|
||||
elm.querySelector('.glyphicon').classList.add('glyphicon-star-empty');
|
||||
}else{
|
||||
elm.classList.remove('unflagged');
|
||||
elm.classList.add('flagged');
|
||||
elm.querySelector('.glyphicon').classList.remove('glyphicon-star-empty');
|
||||
elm.querySelector('.glyphicon').classList.add('glyphicon-star');
|
||||
}
|
||||
|
||||
done();
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var setupToggling = function(elm){
|
||||
elm.addEventListener('click', function(e){
|
||||
toggleStar(e, elm);
|
||||
}, false);
|
||||
}
|
||||
|
||||
var starElms = document.querySelectorAll('.message-star');
|
||||
for(var i=0, len = starElms.length; i<len; i++){
|
||||
setupToggling(starElms[i]);
|
||||
}
|
||||
})();
|
||||
|
||||
// toolbar buttons
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var mailbox = document.getElementById('mailbox').value;
|
||||
var message = document.getElementById('message').value;
|
||||
|
||||
var pendingDeleted = false;
|
||||
var deleteMessage = function(){
|
||||
if(pendingDeleted){
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingDeleted = true;
|
||||
$('#deleteModal .bulk-delete-confirm').button('loading');
|
||||
|
||||
var done = function(){
|
||||
pendingDeleted = false;
|
||||
$('#deleteModal .bulk-delete-confirm').button('reset');
|
||||
$('#deleteModal').modal('hide');
|
||||
}
|
||||
|
||||
fetch('/api/delete', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
_csrf: document.getElementById('_csrf').value,
|
||||
mailbox: mailbox,
|
||||
message: message
|
||||
})
|
||||
})
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(res) {
|
||||
if(res.error){
|
||||
console.error(res.error);
|
||||
return done();
|
||||
}
|
||||
|
||||
window.location.href = '/webmail/' + mailbox;
|
||||
}).catch(function(err){
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
document.querySelector('.bulk-delete-confirm').addEventListener('click', deleteMessage, false);
|
||||
document.querySelector('.bulk-delete-confirm').addEventListener('touch', deleteMessage, false);
|
||||
}, false);
|
||||
|
||||
</script>
|
||||
Referencia en una nueva incidencia
Block a user