Merge pull request #505 from wurstchristoph/drafts
save and open drafts
This commit is contained in:
Коммит
ce22cfd351
|
@ -13,6 +13,7 @@ $app->registerRoutes($this,
|
|||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'page#compose', 'url' => '/compose', 'verb' => 'GET'],
|
||||
['name' => 'accounts#send', 'url' => '/accounts/{accountId}/send', 'verb' => 'POST'],
|
||||
['name' => 'accounts#draft', 'url' => '/accounts/{accountId}/draft', 'verb' => 'POST'],
|
||||
['name' => 'accounts#autoComplete', 'url' => '/accounts/autoComplete', 'verb' => 'GET'],
|
||||
[
|
||||
'name' => 'messages#downloadAttachment',
|
||||
|
|
192
js/mail.js
192
js/mail.js
|
@ -43,13 +43,13 @@ var Mail = {
|
|||
icon: icon
|
||||
}
|
||||
);
|
||||
notification.onclick = function() {
|
||||
notification.onclick = function(x) {
|
||||
Mail.UI.loadMessages(accountId, folderId, false);
|
||||
window.focus();
|
||||
};
|
||||
setTimeout(function () {
|
||||
notification.close();
|
||||
}, 10000);
|
||||
}, 5000);
|
||||
}, checkForNotifications: function() {
|
||||
_.each(Mail.State.accounts, function (a) {
|
||||
var localAccount = Mail.State.folderView.collection.get(a.accountId);
|
||||
|
@ -482,12 +482,60 @@ var Mail = {
|
|||
);
|
||||
},
|
||||
|
||||
openMessage: function (messageId) {
|
||||
openComposer: function(data) {
|
||||
$('#mail_new_message').prop('disabled', true);
|
||||
|
||||
if (Mail.State.composeView === null) {
|
||||
// setup sendmail view
|
||||
Mail.State.composeView = new views.SendMail({
|
||||
el: $('#mail-message'),
|
||||
aliases: Mail.State.accounts,
|
||||
data: data
|
||||
});
|
||||
|
||||
Mail.State.composeView.sentCallback = function () {};
|
||||
} else {
|
||||
Mail.State.composeView.data = data;
|
||||
}
|
||||
|
||||
if (data && data.hasHtmlBody) {
|
||||
Mail.UI.showError(t('mail', 'Opening HTML drafts is not supported yet.'));
|
||||
}
|
||||
|
||||
Mail.State.composeView.attachments.reset();
|
||||
Mail.State.composeView.render();
|
||||
|
||||
// focus 'to' field automatically on clicking New message button
|
||||
$('#to').focus();
|
||||
|
||||
Mail.UI.setMessageActive(null);
|
||||
},
|
||||
|
||||
loadDraft: function(messageId) {
|
||||
var storage = $.localStorage;
|
||||
var draftId = 'draft'
|
||||
+ '.' + Mail.State.currentAccountId.toString()
|
||||
+ '.' + Mail.State.currentFolderId.toString()
|
||||
+ '.' + messageId.toString();
|
||||
if (storage.isSet(draftId)) {
|
||||
return storage.get(draftId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
openMessage: function(messageId) {
|
||||
// Do not reload email when clicking same again
|
||||
if (Mail.State.currentMessageId === messageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if message is a draft
|
||||
var accountId = Mail.State.currentAccountId;
|
||||
var account = Mail.State.folderView.collection.findWhere({id: accountId});
|
||||
var draftsFolder = account.attributes.specialFolders.drafts;
|
||||
var draft = draftsFolder === Mail.State.currentFolderId;
|
||||
|
||||
// close email first
|
||||
// Check if message is open
|
||||
if (Mail.State.currentMessageId !== null) {
|
||||
|
@ -508,6 +556,68 @@ var Mail = {
|
|||
$('#mail_new_message').prop('disabled', false);
|
||||
$('#new-message').hide();
|
||||
|
||||
var self = this;
|
||||
var loadMessageSuccess = function (data) {
|
||||
// load local storage draft
|
||||
var draft = self.loadDraft(messageId);
|
||||
if (draft) {
|
||||
data.replyCc = draft.cc;
|
||||
data.replyBcc = draft.bcc;
|
||||
data.replyBody = draft.body;
|
||||
}
|
||||
|
||||
// Render the message body
|
||||
var source = $("#mail-message-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
var html = template(data);
|
||||
mailBody
|
||||
.html(html)
|
||||
.removeClass('icon-loading');
|
||||
|
||||
Mail.State.messageView.setMessageFlag(messageId, 'unseen', false);
|
||||
|
||||
// HTML mail rendering
|
||||
$('iframe').load(function () {
|
||||
// Expand height to not have two scrollbars
|
||||
$(this).height($(this).contents().find('html').height() + 20);
|
||||
// Fix styling
|
||||
$(this).contents().find('body').css({
|
||||
'margin': '0',
|
||||
'font-weight': 'normal',
|
||||
'font-size': '.8em',
|
||||
'line-height': '1.6em',
|
||||
'font-family': "'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif",
|
||||
'color': '#000'
|
||||
});
|
||||
// Fix font when different font is forced
|
||||
$(this).contents().find('font').prop({
|
||||
'face': 'Open Sans',
|
||||
'color': '#000'
|
||||
});
|
||||
$(this).contents().find('.moz-text-flowed').css({
|
||||
'font-family': 'inherit',
|
||||
'font-size': 'inherit'
|
||||
});
|
||||
// Expand height again after rendering to account for new size
|
||||
$(this).height($(this).contents().find('html').height() + 20);
|
||||
// Grey out previous replies
|
||||
$(this).contents().find('blockquote').css({
|
||||
'-ms-filter': '"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"',
|
||||
'filter': 'alpha(opacity=50)',
|
||||
'opacity': '.5'
|
||||
});
|
||||
// Remove spinner when loading finished
|
||||
$('iframe').parent().removeClass('icon-loading');
|
||||
|
||||
});
|
||||
|
||||
$('textarea').autosize({append: '"\n\n"'});
|
||||
};
|
||||
|
||||
var loadDraftSuccess = function(data) {
|
||||
self.openComposer(data);
|
||||
};
|
||||
|
||||
$.ajax(
|
||||
OC.generateUrl('apps/mail/accounts/{accountId}/folders/{folderId}/messages/{messageId}',
|
||||
{
|
||||
|
@ -518,53 +628,11 @@ var Mail = {
|
|||
data: {},
|
||||
type: 'GET',
|
||||
success: function (data) {
|
||||
// Render the message body
|
||||
var source = $("#mail-message-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
var html = template(data);
|
||||
mailBody
|
||||
.html(html)
|
||||
.removeClass('icon-loading');
|
||||
|
||||
Mail.State.messageView.setMessageFlag(messageId, 'unseen', false);
|
||||
|
||||
// HTML mail rendering
|
||||
$('iframe').load(function () {
|
||||
// Expand height to not have two scrollbars
|
||||
$(this).height($(this).contents().find('html').height() + 20);
|
||||
// Fix styling
|
||||
$(this).contents().find('body').css({
|
||||
'margin': '0',
|
||||
'font-weight': 'normal',
|
||||
'font-size': '.8em',
|
||||
'line-height': '1.6em',
|
||||
'font-family': "'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif",
|
||||
'color': '#000'
|
||||
});
|
||||
// Fix font when different font is forced
|
||||
$(this).contents().find('font').prop({
|
||||
'face': 'Open Sans',
|
||||
'color': '#000'
|
||||
});
|
||||
$(this).contents().find('.moz-text-flowed').css({
|
||||
'font-family': 'inherit',
|
||||
'font-size': 'inherit'
|
||||
});
|
||||
// Expand height again after rendering to account for new size
|
||||
$(this).height($(this).contents().find('html').height() + 20);
|
||||
// Grey out previous replies
|
||||
$(this).contents().find('blockquote').css({
|
||||
'-ms-filter': '"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"',
|
||||
'filter': 'alpha(opacity=50)',
|
||||
'opacity': '.5'
|
||||
});
|
||||
// Remove spinner when loading finished
|
||||
$('iframe').parent().removeClass('icon-loading');
|
||||
|
||||
});
|
||||
|
||||
$('textarea').autosize({append: '"\n\n"'});
|
||||
|
||||
if (draft) {
|
||||
loadDraftSuccess(data);
|
||||
} else {
|
||||
loadMessageSuccess(data);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
Mail.UI.showError(t('mail', 'Error while loading the selected message.'));
|
||||
|
@ -776,29 +844,7 @@ $(document).ready(function () {
|
|||
});
|
||||
|
||||
// new mail message button handling
|
||||
$(document).on('click', '#mail_new_message', function () {
|
||||
$('#mail_new_message').prop('disabled', true);
|
||||
|
||||
if (Mail.State.composeView === null) {
|
||||
// setup sendmail view
|
||||
Mail.State.composeView = new views.SendMail({
|
||||
el: $('#mail-message'),
|
||||
aliases: Mail.State.accounts
|
||||
});
|
||||
|
||||
Mail.State.composeView.sentCallback = function () {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
Mail.State.composeView.attachments.reset();
|
||||
Mail.State.composeView.render();
|
||||
|
||||
// focus 'to' field automatically on clicking New message button
|
||||
$('#to').focus();
|
||||
|
||||
Mail.UI.setMessageActive(null);
|
||||
});
|
||||
$(document).on('click', '#mail_new_message', Mail.UI.openComposer);
|
||||
|
||||
// disable send/reply buttons unless recipient and either subject or message body is filled
|
||||
$(document).on('change input paste keyup', '#to', Mail.UI.toggleSendButton);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global Mail */
|
||||
/* global Mail, OC */
|
||||
$(function () {
|
||||
|
||||
function split(val) {
|
||||
|
@ -53,6 +53,32 @@ $(function () {
|
|||
}
|
||||
});
|
||||
|
||||
function getReplyMessage() {
|
||||
var message = {};
|
||||
|
||||
var replyMessageBody = $('.reply-message-body');
|
||||
var to = $('.reply-message-fields #to');
|
||||
var cc = $('.reply-message-fields #cc');
|
||||
message.body = replyMessageBody.val();
|
||||
message.to = to.val();
|
||||
message.cc = cc.val();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
function saveReplyLocally() {
|
||||
if (Mail.State.currentMessageId === null) {
|
||||
// new message
|
||||
return;
|
||||
}
|
||||
var storage = $.localStorage;
|
||||
storage.set('draft'
|
||||
+ '.' + Mail.State.currentAccountId.toString()
|
||||
+ '.' + Mail.State.currentFolderId.toString()
|
||||
+ '.' + Mail.State.currentMessageId.toString(),
|
||||
getReplyMessage());
|
||||
}
|
||||
|
||||
function sendReply() {
|
||||
//
|
||||
// TODO:
|
||||
|
@ -117,6 +143,8 @@ $(function () {
|
|||
}
|
||||
});
|
||||
|
||||
$(document).on('keyup', '.reply-message-body, #to, #cc', saveReplyLocally);
|
||||
|
||||
$(document).on('click', '.reply-message-send', sendReply);
|
||||
|
||||
// cc/bcc toggling
|
||||
|
|
|
@ -111,6 +111,15 @@ views.Message = Backbone.Marionette.ItemView.extend({
|
|||
data: {},
|
||||
type:'DELETE',
|
||||
success: function () {
|
||||
// delete local storage draft
|
||||
var storage = $.localStorage;
|
||||
var draftId = 'draft'
|
||||
+ '.' + Mail.State.currentAccountId.toString()
|
||||
+ '.' + Mail.State.currentFolderId.toString()
|
||||
+ '.' + thisModel.id;
|
||||
if (storage.isSet(draftId)) {
|
||||
storage.remove(draftId);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Mail.UI.showError(t('mail', 'Error while deleting message.'));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global Backbone, Handlebars, models */
|
||||
/* global Backbone, Handlebars, models, OC, Mail */
|
||||
|
||||
var views = views || {};
|
||||
|
||||
|
@ -11,16 +11,32 @@ views.SendMail = Backbone.View.extend({
|
|||
|
||||
aliases: null,
|
||||
currentAccountId: null,
|
||||
data: null,
|
||||
draftIntervalIMAP: 1500,
|
||||
draftIntervalLocal: 100,
|
||||
draftTimerIMAP: null,
|
||||
draftTimerLocal: null,
|
||||
draftUID: null,
|
||||
|
||||
events: {
|
||||
"click #new-message-send" : "sendMail",
|
||||
"click #new-message-draft" : "saveDraft",
|
||||
"keypress #new-message-body" : "handleKeyPress",
|
||||
"keyup #new-message-body": "handleKeyUp",
|
||||
"keyup #to": "handleKeyUp",
|
||||
"keyup #cc": "handleKeyUp",
|
||||
"keyup #bcc": "handleKeyUp",
|
||||
"keyup #subject": "handleKeyUp",
|
||||
"click .mail_account" : "changeAlias"
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.attachments = new models.Attachments();
|
||||
this.aliases = options.aliases;
|
||||
if (options.data) {
|
||||
this.data = options.data;
|
||||
this.draftUID = options.data.id;
|
||||
}
|
||||
this.el = options.el;
|
||||
this.currentAccountId = this.aliases[0].accountId;
|
||||
},
|
||||
|
@ -30,16 +46,50 @@ views.SendMail = Backbone.View.extend({
|
|||
},
|
||||
|
||||
handleKeyPress: function(event) {
|
||||
var key = event.keyCode || event.which;
|
||||
var sendBtnState = $('#new-message-send').attr('disabled');
|
||||
|
||||
// check for ctrl+enter
|
||||
if (event.keyCode === 13 && event.ctrlKey) {
|
||||
var sendBtnState = $('#new-message-send').attr('disabled');
|
||||
if (key === 13 && event.ctrlKey) {
|
||||
if (sendBtnState === undefined) {
|
||||
this.sendMail();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
handleKeyUp: function() {
|
||||
clearTimeout(this.draftTimerIMAP);
|
||||
clearTimeout(this.draftIntervalLocal);
|
||||
var self = this;
|
||||
this.draftTimerIMAP = setTimeout(function() {
|
||||
self.saveDraft();
|
||||
}, this.draftIntervalIMAP);
|
||||
this.draftTimerLocal = setTimeout(function() {
|
||||
self.saveDraftLocally();
|
||||
}, this.draftIntervalLocal);
|
||||
},
|
||||
|
||||
getMessage: function() {
|
||||
var message = {};
|
||||
var newMessageBody = $('#new-message-body');
|
||||
var to = $('#to');
|
||||
var cc = $('#cc');
|
||||
var bcc = $('#bcc');
|
||||
var subject = $('#subject');
|
||||
|
||||
message.body = newMessageBody.val();
|
||||
message.to = to.val();
|
||||
message.cc = cc.val();
|
||||
message.bcc = bcc.val();
|
||||
message.subject = subject.val();
|
||||
message.attachments = this.attachments.toJSON();
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
sendMail: function() {
|
||||
clearTimeout(this.draftTimerIMAP);
|
||||
//
|
||||
// TODO:
|
||||
// - input validation
|
||||
|
@ -66,18 +116,20 @@ views.SendMail = Backbone.View.extend({
|
|||
newMessageSend.prop('disabled', true);
|
||||
newMessageSend.val(t('mail', 'Sending …'));
|
||||
|
||||
var message = this.getMessage();
|
||||
var self = this;
|
||||
// send the mail
|
||||
$.ajax({
|
||||
url:OC.generateUrl('/apps/mail/accounts/{accountId}/send', {accountId: this.currentAccountId}),
|
||||
type: 'POST',
|
||||
data:{
|
||||
'to': to.val(),
|
||||
'cc': cc.val(),
|
||||
'bcc': bcc.val(),
|
||||
'subject': subject.val(),
|
||||
'body':newMessageBody.val(),
|
||||
'attachments': self.attachments.toJSON()
|
||||
'to': message.to,
|
||||
'cc': message.cc,
|
||||
'bcc': message.bcc,
|
||||
'subject': message.subject,
|
||||
'body': message.body,
|
||||
'attachments': message.attachments,
|
||||
'draftUID' : this.draftUID
|
||||
},
|
||||
success:function () {
|
||||
OC.Notification.showTemporary(t('mail', 'Message sent!'));
|
||||
|
@ -95,6 +147,11 @@ views.SendMail = Backbone.View.extend({
|
|||
$('#subject').val('');
|
||||
$('#new-message-body').val('');
|
||||
self.attachments.reset();
|
||||
if (self.draftUID !== null) {
|
||||
// the sent message was a draft
|
||||
Mail.State.messageView.collection.remove({id: self.draftUID});
|
||||
self.draftUID = null;
|
||||
}
|
||||
},
|
||||
error: function (jqXHR) {
|
||||
OC.Notification.showTemporary(jqXHR.responseJSON.message);
|
||||
|
@ -117,10 +174,81 @@ views.SendMail = Backbone.View.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
saveDraftLocally: function() {
|
||||
var storage = $.localStorage;
|
||||
storage.set("draft", "default", this.getMessage());
|
||||
},
|
||||
|
||||
saveDraft: function() {
|
||||
clearTimeout(this.draftTimerIMAP);
|
||||
//
|
||||
// TODO:
|
||||
// - input validation
|
||||
// - feedback on success
|
||||
// - undo lie - very important
|
||||
//
|
||||
|
||||
var message = this.getMessage();
|
||||
var self = this;
|
||||
// send the mail
|
||||
$.ajax({
|
||||
url:OC.generateUrl('/apps/mail/accounts/{accountId}/draft', {accountId: this.currentAccountId}),
|
||||
beforeSend:function () {
|
||||
OC.msg.startAction('#new-message-msg', "");
|
||||
},
|
||||
type: 'POST',
|
||||
data: {
|
||||
'to': message.to,
|
||||
'cc': message.cc,
|
||||
'bcc': message.bcc,
|
||||
'subject': message.subject,
|
||||
'body': message.body,
|
||||
'uid': self.draftUID
|
||||
},
|
||||
success: function (data) {
|
||||
if (self.draftUID !== null) {
|
||||
// update UID in message list
|
||||
var message = Mail.State.messageView.collection.findWhere({id: self.draftUID});
|
||||
if (message) {
|
||||
message.set({id: data.uid});
|
||||
Mail.State.messageView.collection.set([message], {remove: false});
|
||||
}
|
||||
}
|
||||
self.draftUID = data.uid;
|
||||
OC.msg.finishedAction('#new-message-msg', {
|
||||
status: 'success',
|
||||
data: {
|
||||
message: t('mail', 'Draft saved!')
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (jqXHR) {
|
||||
OC.msg.finishedAction('#new-message-msg', {
|
||||
status: 'error',
|
||||
data: {
|
||||
message: jqXHR.responseJSON.message
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var source = $("#new-message-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
var html = template({aliases: this.aliases});
|
||||
var data = {
|
||||
aliases: this.aliases
|
||||
};
|
||||
|
||||
// draft data
|
||||
if (this.data) {
|
||||
data.to = this.data.toEmail;
|
||||
data.subject = this.data.subject;
|
||||
data.message = this.data.body;
|
||||
}
|
||||
|
||||
var html = template(data);
|
||||
|
||||
this.$el.html(html);
|
||||
|
||||
|
|
|
@ -267,6 +267,26 @@ class Account {
|
|||
return $specialFoldersIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "drafts" mailbox
|
||||
*
|
||||
* @return Mailbox The best candidate for the "drafts" inbox
|
||||
*/
|
||||
public function getDraftsFolder() {
|
||||
// check for existense
|
||||
$draftsFolder = $this->getSpecialFolder('drafts', true);
|
||||
if (count($draftsFolder) === 0) {
|
||||
// drafts folder does not exist - let's create one
|
||||
$conn = $this->getImapConnection();
|
||||
// TODO: also search for translated drafts mailboxes
|
||||
$conn->createMailbox('Drafts', array(
|
||||
'special_use' => array('drafts'),
|
||||
));
|
||||
return $this->guessBestMailBox($this->listMailboxes('Drafts'));
|
||||
}
|
||||
return $draftsFolder[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "sent mail" mailbox
|
||||
*
|
||||
|
@ -327,6 +347,18 @@ class Account {
|
|||
array('message' => $messageId, 'mailbox' => $sourceFolderId));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $messageId
|
||||
*/
|
||||
public function deleteDraft($messageId) {
|
||||
$draftsFolder = $this->getDraftsFolder();
|
||||
|
||||
$IDs = new \Horde_Imap_Client_Ids($messageId);
|
||||
$draftsMailBox = new \Horde_Imap_Client_Mailbox($draftsFolder->getFolderId(), true);
|
||||
$this->getImapConnection()->expunge($draftsMailBox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'best' mailbox guess
|
||||
*
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
namespace OCA\Mail\Controller;
|
||||
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Mime_Headers_Date;
|
||||
use Horde_Mime_Mail;
|
||||
use Horde_Mime_Part;
|
||||
use Horde_Mail_Rfc822_Address;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
|
@ -141,8 +144,6 @@ class AccountsController extends Controller {
|
|||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @param $accountId
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function destroy($accountId) {
|
||||
try {
|
||||
|
@ -229,6 +230,7 @@ class AccountsController extends Controller {
|
|||
$to = $this->params('to');
|
||||
$cc = $this->params('cc');
|
||||
$bcc = $this->params('bcc');
|
||||
$draftUID = $this->params('draftUID');
|
||||
|
||||
$dbAccount = $this->mapper->find($this->currentUserId, $accountId);
|
||||
$account = new Account($dbAccount);
|
||||
|
@ -271,7 +273,7 @@ class AccountsController extends Controller {
|
|||
$headers['To'] = $to;
|
||||
|
||||
// build mime body
|
||||
$mail = new \Horde_Mime_Mail();
|
||||
$mail = new Horde_Mime_Mail();
|
||||
$mail->addHeaders($headers);
|
||||
$mail->setBody($body);
|
||||
|
||||
|
@ -307,7 +309,19 @@ class AccountsController extends Controller {
|
|||
// save the message in the sent folder
|
||||
$sentFolder = $account->getSentFolder();
|
||||
$raw = stream_get_contents($mail->getRaw());
|
||||
$sentFolder->saveMessage($raw, [Horde_Imap_Client::FLAG_SEEN]);
|
||||
$sentFolder->saveMessage($raw, [
|
||||
Horde_Imap_Client::FLAG_SEEN
|
||||
]);
|
||||
|
||||
// delete draft message
|
||||
if (!is_null($draftUID)) {
|
||||
$draftsFolder = $account->getDraftsFolder();
|
||||
$folderId = $draftsFolder->getFolderId();
|
||||
$this->logger->debug("deleting sent draft <$draftUID> in folder <$folderId>");
|
||||
$draftsFolder->setMessageFlag($draftUID, \Horde_Imap_Client::FLAG_DELETED, true);
|
||||
$account->deleteDraft($draftUID);
|
||||
$this->logger->debug("sent draft <$draftUID> deleted");
|
||||
}
|
||||
} catch (\Horde_Exception $ex) {
|
||||
$this->logger->error('Sending mail failed: ' . $ex->getMessage());
|
||||
return new JSONResponse(
|
||||
|
@ -319,6 +333,86 @@ class AccountsController extends Controller {
|
|||
return new JSONResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @param int $accountId
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function draft($accountId) {
|
||||
$subject = $this->params('subject');
|
||||
$body = $this->params('body');
|
||||
$to = $this->params('to');
|
||||
$cc = $this->params('cc');
|
||||
$bcc = $this->params('bcc');
|
||||
$uid = $this->params('uid');
|
||||
|
||||
if (!is_null($uid)) {
|
||||
$this->logger->info("Saving a new draft in accout <$accountId>");
|
||||
} else {
|
||||
$this->logger->info("Updating draft <$uid> in account <$accountId>");
|
||||
}
|
||||
|
||||
$dbAccount = $this->mapper->find($this->currentUserId, $accountId);
|
||||
$account = new Account($dbAccount);
|
||||
|
||||
// get sender data
|
||||
$headers = array();
|
||||
$from = new Horde_Mail_Rfc822_Address($account->getEMailAddress());
|
||||
$from->personal = $account->getName();
|
||||
$headers['From']= $from;
|
||||
$headers['Subject'] = $subject;
|
||||
|
||||
if (trim($cc) !== '') {
|
||||
$headers['Cc'] = trim($cc);
|
||||
}
|
||||
if (trim($bcc) !== '') {
|
||||
$headers['Bcc'] = trim($bcc);
|
||||
}
|
||||
|
||||
$headers['To'] = $to;
|
||||
$headers['Date'] = Horde_Mime_Headers_Date::create();
|
||||
|
||||
// build mime body
|
||||
$mail = new Horde_Mime_Mail();
|
||||
$mail->addHeaders($headers);
|
||||
$bodyPart = new Horde_Mime_Part();
|
||||
$bodyPart->appendContents($body, [
|
||||
'encoding' => \Horde_Mime_Part::ENCODE_8BIT
|
||||
]);
|
||||
$mail->setBasePart($bodyPart);
|
||||
|
||||
// create transport and save message
|
||||
try {
|
||||
// save the message in the drafts folder
|
||||
$draftsFolder = $account->getDraftsFolder();
|
||||
$raw = stream_get_contents($mail->getRaw());
|
||||
$newUid = $draftsFolder->saveDraft($raw);
|
||||
|
||||
// delete old version if one exists
|
||||
if (!is_null($uid)) {
|
||||
$folderId = $draftsFolder->getFolderId();
|
||||
$this->logger->debug("deleting outdated draft <$uid> in folder <$folderId>");
|
||||
$draftsFolder->setMessageFlag($uid, \Horde_Imap_Client::FLAG_DELETED, true);
|
||||
$account->deleteDraft($uid);
|
||||
$this->logger->debug("draft <$uid> deleted");
|
||||
}
|
||||
} catch (\Horde_Exception $ex) {
|
||||
$this->logger->error('Saving draft failed: ' . $ex->getMessage());
|
||||
return new JSONResponse(
|
||||
[
|
||||
'message' => $ex->getMessage()
|
||||
],
|
||||
Http::STATUS_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
return new JSONResponse(
|
||||
[
|
||||
'uid' => $newUid
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @param string $term
|
||||
|
|
|
@ -121,9 +121,8 @@ class MessagesController extends Controller
|
|||
|
||||
$account = $this->getAccount();
|
||||
$m = $mailBox->getMessage($id);
|
||||
$json = $m->getFullMessage($account->getEmail(), ($mailBox->getSpecialRole() === 'sent'));
|
||||
|
||||
$json['senderImage'] = $this->contactsIntegration->getPhoto($json['fromEmail']);
|
||||
$json = $m->getFullMessage($account->getEmail(), $mailBox->getSpecialRole());
|
||||
$json['senderImage'] = $this->contactsIntegration->getPhoto($m->getFromEmail());
|
||||
if (isset($json['hasHtmlBody'])){
|
||||
$json['htmlBodyUrl'] = $this->buildHtmlBodyUrl($accountId, $folderId, $id);
|
||||
}
|
||||
|
@ -132,7 +131,7 @@ class MessagesController extends Controller
|
|||
$json['attachment'] = $this->enrichDownloadUrl($accountId, $folderId, $id, $json['attachment']);
|
||||
}
|
||||
if (isset($json['attachments'])) {
|
||||
$json['attachments'] = array_map(function($a) use($accountId, $folderId, $id) {
|
||||
$json['attachments'] = array_map(function($a) use ($accountId, $folderId, $id) {
|
||||
return $this->enrichDownloadUrl($accountId, $folderId, $id, $a);
|
||||
}, $json['attachments']);
|
||||
}
|
||||
|
|
|
@ -337,6 +337,26 @@ class Mailbox {
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save draft
|
||||
*
|
||||
* @param string $rawBody
|
||||
* @return int UID of the saved draft
|
||||
*/
|
||||
public function saveDraft($rawBody) {
|
||||
|
||||
$uids = $this->conn->append($this->mailBox, [
|
||||
[
|
||||
'data' => $rawBody,
|
||||
'flags' => [
|
||||
Horde_Imap_Client::FLAG_DRAFT,
|
||||
Horde_Imap_Client::FLAG_SEEN
|
||||
]
|
||||
]
|
||||
]);
|
||||
return $uids->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @param string $flag
|
||||
|
|
|
@ -349,7 +349,7 @@ class Message {
|
|||
/**
|
||||
* @param string $ownMail
|
||||
*/
|
||||
public function getFullMessage($ownMail, $fromSent = false) {
|
||||
public function getFullMessage($ownMail, $specialRole=null) {
|
||||
$mailBody = $this->plainMessage;
|
||||
|
||||
$data = $this->getListArray();
|
||||
|
@ -358,7 +358,7 @@ class Message {
|
|||
} else {
|
||||
$mailBody = $this->htmlService->convertLinks($mailBody);
|
||||
list($mailBody, $signature) = $this->htmlService->parseMailBody($mailBody);
|
||||
$data['body'] = nl2br($mailBody);
|
||||
$data['body'] = $specialRole === 'drafts' ? $mailBody : nl2br($mailBody);
|
||||
$data['signature'] = $signature;
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ class Message {
|
|||
$data['attachments'] = $this->attachments;
|
||||
}
|
||||
|
||||
if ($fromSent) {
|
||||
if ($specialRole === 'sent') {
|
||||
$data['replyToList'] = $this->getToList();
|
||||
$data['replyCcList'] = $this->getCCList();
|
||||
} else {
|
||||
|
|
|
@ -156,17 +156,17 @@ script('mail', 'jquery-visibility');
|
|||
class="hidden"
|
||||
{{/unless}}
|
||||
>
|
||||
<input type="text" name="cc" id="cc" class="recipient-autocomplete"
|
||||
<input type="text" name="cc" id="cc" value="{{replyCc}}" class="recipient-autocomplete"
|
||||
value="{{printAddressListPlain replyCcList}}" />
|
||||
<label id="cc-label" for="cc" class="transparency"><?php p($l->t('cc')); ?></label>
|
||||
<!--
|
||||
<input type="text" name="bcc" id="bcc" class="recipient-autocomplete" />
|
||||
<input type="text" name="bcc" id="bcc" value="{{replyBcc}}" class="recipient-autocomplete" />
|
||||
<label id="bcc-label" for="bcc" class="transparency"><?php p($l->t('bcc')); ?></label>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<textarea name="body" class="reply-message-body"
|
||||
placeholder="<?php p($l->t('Reply …')); ?>"></textarea>
|
||||
placeholder="<?php p($l->t('Reply …')); ?>">{{replyBody}}</textarea>
|
||||
<input class="reply-message-send primary" type="submit" value="<?php p($l->t('Reply')) ?>" disabled>
|
||||
</div>
|
||||
<div class="reply-message-more">
|
||||
|
@ -202,18 +202,18 @@ script('mail', 'jquery-visibility');
|
|||
<div id="new-message-fields">
|
||||
<a href="#" id="new-message-cc-bcc-toggle"
|
||||
class="transparency"><?php p($l->t('+ cc/bcc')); ?></a>
|
||||
<input type="text" name="to" id="to" class="recipient-autocomplete" />
|
||||
<input type="text" name="to" id="to" value="{{to}}" class="recipient-autocomplete" />
|
||||
<label id="to-label" for="to" class="transparency"><?php p($l->t('to')); ?></label>
|
||||
<div id="new-message-cc-bcc">
|
||||
<input type="text" name="cc" id="cc" class="recipient-autocomplete" />
|
||||
<input type="text" name="cc" id="cc" value="{{cc}}" class="recipient-autocomplete" />
|
||||
<label id="cc-label" for="cc" class="transparency"><?php p($l->t('cc')); ?></label>
|
||||
<input type="text" name="bcc" id="bcc" class="recipient-autocomplete" />
|
||||
<input type="text" name="bcc" id="bcc" value="{{bcc}}" class="recipient-autocomplete" />
|
||||
<label id="bcc-label" for="bcc" class="transparency"><?php p($l->t('bcc')); ?></label>
|
||||
</div>
|
||||
<input type="text" name="subject" id="subject"
|
||||
<input type="text" name="subject" id="subject" value="{{subject}}"
|
||||
placeholder="<?php p($l->t('Subject')); ?>" />
|
||||
<textarea name="body" id="new-message-body"
|
||||
placeholder="<?php p($l->t('Message …')); ?>"></textarea>
|
||||
placeholder="<?php p($l->t('Message …')); ?>">{{message}}</textarea>
|
||||
<input id="new-message-send" class="send primary" type="submit" value="<?php p($l->t('Send')) ?>" disabled>
|
||||
</div>
|
||||
<div id="new-message-attachments">
|
||||
|
|
Загрузка…
Ссылка в новой задаче