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
- [core] can now invite attendees to exceptions only (#2561)
- [web] display freebusy information of owner in appointment editor
- [web] register SOGo as a handler for the mailto scheme (#1223)
Enhancements
-

View File

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

View File

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

View File

@ -141,6 +141,12 @@
// if none of the above states are matched, use this as the fallback
$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
*/
stateMailbox.$inject = ['$q', '$state', '$stateParams', 'stateAccount', 'decodeUriFilter', 'Mailbox'];
function stateMailbox($q, $state, $stateParams, stateAccount, decodeUriFilter, Mailbox) {
stateMailbox.$inject = ['$q', '$stateParams', 'stateAccount', 'decodeUriFilter', 'Mailbox'];
function stateMailbox($q, $stateParams, stateAccount, decodeUriFilter, Mailbox) {
var mailbox,
mailboxId = decodeUriFilter($stateParams.mailboxId),
_find;

View File

@ -99,27 +99,33 @@
/**
* @ngInject
*/
stateAccounts.$inject = ['$q', 'Account'];
function stateAccounts($q, Account) {
stateAccounts.$inject = ['$window', '$q', 'Account'];
function stateAccounts($window, $q, Account) {
var accounts, promises = [];
if (window &&
window.opener &&
window.opener.$mailboxController) {
if ($window &&
$window.opener &&
$window.opener.$mailboxController) {
// Mail accounts are available from the parent window
accounts = window.opener.$mailboxController.accounts;
accounts = $window.opener.$mailboxController.accounts;
return $q.when(accounts);
}
else {
accounts = Account.$findAll();
// Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) {
var mailboxes = account.$getMailboxes();
promises.push(mailboxes.then(function(objects) {
return account;
}));
return Account.$findAll().then(function(accounts) {
// Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) {
var mailboxes = account.$getMailboxes();
if (i === 0)
// 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
*/
stateMailbox.$inject = ['$stateParams', 'stateAccount', 'decodeUriFilter'];
function stateMailbox($stateParams, stateAccount, decodeUriFilter) {
var mailboxId = decodeUriFilter($stateParams.mailboxId),
stateMailbox.$inject = ['$q', '$state', '$stateParams', 'stateAccount', 'decodeUriFilter', 'Mailbox'];
function stateMailbox($q, $state, $stateParams, stateAccount, decodeUriFilter, Mailbox) {
var mailbox,
mailboxId = decodeUriFilter($stateParams.mailboxId),
_find;
// Recursive find function
_find = function(mailboxes) {
var mailbox = _.find(mailboxes, function(o) {
@ -154,15 +162,31 @@
}
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
*/
stateNewMessage.$inject = ['stateAccount'];
function stateNewMessage(stateAccount) {
return stateAccount.$newMessage();
stateNewMessage.$inject = ['$urlService', 'stateAccount'];
function stateNewMessage($urlService, stateAccount) {
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);
};
/**
* @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
* @memberof Message.prototype

View File

@ -193,27 +193,8 @@
href = $event.target.attributes.href.value;
match = /^mailto:([^\?]+)/.exec(href);
if (match) {
// Recipients
to = _.map(decodeURIComponent(match[1]).split(','), function(email) {
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
delete $event.target.attributes.target;
this.newMessage($event, href); // will stop event propagation
}
}
};
@ -359,13 +340,12 @@
$window.close();
};
this.newMessage = function($event, editableContent) {
this.account.$newMessage().then(function(message) {
angular.extend(message.editable, editableContent);
_showMailEditor($event, message);
});
this.newMessage = function($event, mailto) {
$event.stopPropagation();
$event.preventDefault();
this.account.$newMessage({ mailto: mailto }).then(function(message) {
_showMailEditor($event, message);
});
};
this.toggleRawSource = function($event) {