register web app as a handler for mailto scheme

Fixes #1223
pull/237/merge
Francis Lachapelle 2017-08-29 11:45:24 -04:00
parent c277c397e7
commit 5e5e52e3f1
7 changed files with 99 additions and 52 deletions

1
NEWS
View File

@ -4,6 +4,7 @@
New features New features
- [core] can now invite attendees to exceptions only (#2561) - [core] can now invite attendees to exceptions only (#2561)
- [web] display freebusy information of owner in appointment editor - [web] display freebusy information of owner in appointment editor
- [web] register SOGo as a handler for the mailto scheme (#1223)
Enhancements Enhancements
- -

View File

@ -12,8 +12,7 @@
nv-file-drop="nv-file-drop" nv-file-drop="nv-file-drop"
nv-file-over="nv-file-over" nv-file-over="nv-file-over"
over-class="sg-over-dropzone" over-class="sg-over-dropzone"
uploader="editor.uploader" uploader="editor.uploader">
ng-controller="navController">
<form name="messageForm"> <form name="messageForm">
<md-toolbar> <md-toolbar>
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">

View File

@ -300,7 +300,7 @@
* @desc Prepare a new Message object associated to the appropriate mailbox. * @desc Prepare a new Message object associated to the appropriate mailbox.
* @returns a promise of the HTTP operations * @returns a promise of the HTTP operations
*/ */
Account.prototype.$newMessage = function() { Account.prototype.$newMessage = function(options) {
var _this = this; var _this = this;
// Query account for draft folder and draft UID // Query account for draft folder and draft UID
@ -314,6 +314,8 @@
Account.$log.debug('New message (edit): ' + JSON.stringify(data, undefined, 2)); Account.$log.debug('New message (edit): ' + JSON.stringify(data, undefined, 2));
angular.extend(message.editable, data); angular.extend(message.editable, data);
message.isNew = true; message.isNew = true;
if (options && options.mailto)
message.$parseMailto(options.mailto);
return message; return message;
}); });
}); });

View File

@ -141,6 +141,12 @@
// if none of the above states are matched, use this as the fallback // if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/Mail/0/inbox'); $urlRouterProvider.otherwise('/Mail/0/inbox');
// Try to register SOGo has an handler for mailto: links
if (navigator && navigator.registerProtocolHandler) {
var mailtoURL = window.location.origin + window.ApplicationBaseURL + 'UIxMailPopupView#!/Mail/0/INBOX/new?%s';
navigator.registerProtocolHandler('mailto', mailtoURL, 'SOGo');
}
} }
/** /**
@ -178,8 +184,8 @@
/** /**
* @ngInject * @ngInject
*/ */
stateMailbox.$inject = ['$q', '$state', '$stateParams', 'stateAccount', 'decodeUriFilter', 'Mailbox']; stateMailbox.$inject = ['$q', '$stateParams', 'stateAccount', 'decodeUriFilter', 'Mailbox'];
function stateMailbox($q, $state, $stateParams, stateAccount, decodeUriFilter, Mailbox) { function stateMailbox($q, $stateParams, stateAccount, decodeUriFilter, Mailbox) {
var mailbox, var mailbox,
mailboxId = decodeUriFilter($stateParams.mailboxId), mailboxId = decodeUriFilter($stateParams.mailboxId),
_find; _find;

View File

@ -99,27 +99,33 @@
/** /**
* @ngInject * @ngInject
*/ */
stateAccounts.$inject = ['$q', 'Account']; stateAccounts.$inject = ['$window', '$q', 'Account'];
function stateAccounts($q, Account) { function stateAccounts($window, $q, Account) {
var accounts, promises = []; var accounts, promises = [];
if (window && if ($window &&
window.opener && $window.opener &&
window.opener.$mailboxController) { $window.opener.$mailboxController) {
// Mail accounts are available from the parent window // Mail accounts are available from the parent window
accounts = window.opener.$mailboxController.accounts; accounts = $window.opener.$mailboxController.accounts;
return $q.when(accounts); return $q.when(accounts);
} }
else { else {
accounts = Account.$findAll(); return Account.$findAll().then(function(accounts) {
// Fetch list of mailboxes for each account // Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) { angular.forEach(accounts, function(account, i) {
var mailboxes = account.$getMailboxes(); var mailboxes = account.$getMailboxes();
promises.push(mailboxes.then(function(objects) { if (i === 0)
return account; // Make sure we have the list of mailboxes of the first account
})); promises.push(mailboxes.then(function(objects) {
return account;
}));
else
// Don't wait for external accounts
promises.push(account);
});
return $q.all(promises);
}); });
return $q.all(promises);
} }
} }
@ -136,10 +142,12 @@
/** /**
* @ngInject * @ngInject
*/ */
stateMailbox.$inject = ['$stateParams', 'stateAccount', 'decodeUriFilter']; stateMailbox.$inject = ['$q', '$state', '$stateParams', 'stateAccount', 'decodeUriFilter', 'Mailbox'];
function stateMailbox($stateParams, stateAccount, decodeUriFilter) { function stateMailbox($q, $state, $stateParams, stateAccount, decodeUriFilter, Mailbox) {
var mailboxId = decodeUriFilter($stateParams.mailboxId), var mailbox,
mailboxId = decodeUriFilter($stateParams.mailboxId),
_find; _find;
// Recursive find function // Recursive find function
_find = function(mailboxes) { _find = function(mailboxes) {
var mailbox = _.find(mailboxes, function(o) { var mailbox = _.find(mailboxes, function(o) {
@ -154,15 +162,31 @@
} }
return mailbox; return mailbox;
}; };
return _find(stateAccount.$mailboxes);
mailbox = _find(stateAccount.$mailboxes);
if (mailbox) {
mailbox.$topIndex = 0;
mailbox.selectFolder();
return mailbox;
}
else
// Mailbox not found
return $q.reject("Mailbox doesn't exist");
} }
/** /**
* @ngInject * @ngInject
*/ */
stateNewMessage.$inject = ['stateAccount']; stateNewMessage.$inject = ['$urlService', 'stateAccount'];
function stateNewMessage(stateAccount) { function stateNewMessage($urlService, stateAccount) {
return stateAccount.$newMessage(); var mailto, params = $urlService.search();
if (params) {
mailto = _.find(_.keys(params), function(k) {
return /^mailto:/i.test(k);
});
}
return stateAccount.$newMessage({ mailto: mailto });
} }
/** /**

View File

@ -544,6 +544,41 @@
return this.$unwrap(futureMessageData); return this.$unwrap(futureMessageData);
}; };
/**
* @function $parseMailto
* @memberof Message.prototype
* @desc Extend the editable content of the message with the
* information parsed from the specified "mailto:" link.
*/
Message.prototype.$parseMailto = function(mailto) {
var to, data, match = /^mailto:([^\?]+)/.exec(mailto);
if (match) {
// Recipients
to = _.map(decodeURIComponent(match[1]).split(','), function(email) {
return '<' + email.trim() + '>';
});
data = { to: to };
// Subject & body
_.forEach(['subject', 'body'], function(param) {
var re = new RegExp(param + '=([^&]+)');
param = (param == 'body')? 'text' : param;
match = re.exec(mailto);
if (match)
data[param] = decodeURIComponent(match[1]);
});
// Other Recipients
_.forEach(['cc', 'bcc'], function(param) {
var re = new RegExp(param + '=([^&]+)');
match = re.exec(mailto);
if (match)
data[param] = _.map(decodeURIComponent(match[1]).split(','), function(email) {
return '<' + email.trim() + '>';
});
});
angular.extend(this.editable, data);
}
};
/** /**
* @function $reply * @function $reply
* @memberof Message.prototype * @memberof Message.prototype

View File

@ -193,27 +193,8 @@
href = $event.target.attributes.href.value; href = $event.target.attributes.href.value;
match = /^mailto:([^\?]+)/.exec(href); match = /^mailto:([^\?]+)/.exec(href);
if (match) { if (match) {
// Recipients delete $event.target.attributes.target;
to = _.map(decodeURIComponent(match[1]).split(','), function(email) { this.newMessage($event, href); // will stop event propagation
return '<' + email + '>';
});
data = { to: to };
// Subject & body
_.forEach(['subject', 'body'], function(param) {
var re = new RegExp(param + '=([^&]+)');
param = (param == 'body')? 'text' : param;
match = re.exec(href);
if (match)
data[param] = [decodeURIComponent(match[1])];
});
// Recipients
_.forEach(['cc', 'bcc'], function(param) {
var re = new RegExp(param + '=([^&]+)');
match = re.exec(href);
if (match)
data[param] = [decodeURIComponent(match[1])];
});
this.newMessage($event, data); // will stop event propagation
} }
} }
}; };
@ -359,13 +340,12 @@
$window.close(); $window.close();
}; };
this.newMessage = function($event, editableContent) { this.newMessage = function($event, mailto) {
this.account.$newMessage().then(function(message) {
angular.extend(message.editable, editableContent);
_showMailEditor($event, message);
});
$event.stopPropagation(); $event.stopPropagation();
$event.preventDefault(); $event.preventDefault();
this.account.$newMessage({ mailto: mailto }).then(function(message) {
_showMailEditor($event, message);
});
}; };
this.toggleRawSource = function($event) { this.toggleRawSource = function($event) {