Initial mail editor

This commit is contained in:
Francis Lachapelle
2014-12-11 11:24:22 -05:00
parent fdf875fb73
commit 39866e75c9
13 changed files with 533 additions and 268 deletions
@@ -12,7 +12,13 @@
// Data is immediately available
if (typeof futureAccountData.then !== 'function') {
angular.extend(this, futureAccountData);
Account.$log.debug('Account:' + JSON.stringify(futureAccountData, undefined, 2));
_.each(this.identities, function(identity) {
if (identity.fullName)
identity.full = identity.fullName + ' <' + identity.email + '>';
else
identity.full = '<' + identity.email + '>';
});
Account.$log.debug('Account: ' + JSON.stringify(futureAccountData, undefined, 2));
}
else {
// The promise will be unwrapped first
@@ -25,13 +31,14 @@
* @desc The factory we'll use to register with Angular
* @returns the Account constructor
*/
Account.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgMailbox', function($q, $timeout, $log, Settings, Resource, Mailbox) {
Account.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgMailbox', 'sgMessage', function($q, $timeout, $log, Settings, Resource, Mailbox, Message) {
angular.extend(Account, {
$q: $q,
$timeout: $timeout,
$log: $log,
$$resource: new Resource(Settings.baseURL, Settings.activeUser),
$Mailbox: Mailbox
$Mailbox: Mailbox,
$Message: Message
});
return Account; // return constructor
@@ -66,13 +73,95 @@
* @returns a promise of the HTTP operation
*/
Account.prototype.$getMailboxes = function() {
var _this = this;
var _this = this,
deferred = Account.$q.defer();
var mailboxes = Account.$Mailbox.$find(this).then(function(data) {
_this.$mailboxes = data;
if (this.$mailboxes) {
deferred.resolve(this.$mailboxes);
}
else {
Account.$Mailbox.$find(this).then(function(data) {
_this.$mailboxes = data;
deferred.resolve(_this.$mailboxes);
});
}
return deferred.promise;
};
Account.prototype.$flattenMailboxes = function() {
var _this = this,
allMailboxes = [],
_visit = function(level, mailboxes) {
_.each(mailboxes, function(o) {
allMailboxes.push({ id: o.id, path: o.path, name: o.name, level: level });
if (o.children && o.children.length > 0) {
_visit(level+1, o.children);
}
});
};
if (this.$$flattenMailboxes) {
allMailboxes = this.$$flattenMailboxes;
}
else {
_visit(0, this.$mailboxes);
_this.$$flattenMailboxes = allMailboxes;
}
return allMailboxes;
};
Account.prototype.$getMailboxByType = function(type) {
var mailbox,
// Recursive find function
_find = function(mailboxes) {
var mailbox = _.find(mailboxes, function(o) {
return o.type == type;
});
if (!mailbox) {
angular.forEach(mailboxes, function(o) {
if (!mailbox && o.children && o.children.length > 0) {
mailbox = _find(o.children);
}
});
}
return mailbox;
};
mailbox = _find(this.mailboxes);
console.debug(mailbox);
console.debug(this.specialMailboxes);
};
/**
* @function $newMessage
* @memberof Account.prototype
* @desc Prepare a new Message object associated to the appropriate mailbox.
* @returns a promise of the HTTP operations
*/
Account.prototype.$newMessage = function() {
var _this = this,
deferred = Account.$q.defer(),
message;
// Query account for draft folder and draft UID
Account.$$resource.fetch(this.id, 'compose').then(function(data) {
message = new Account.$Message(data.accountId, data.mailboxPath, data);
// Fetch draft initial data
Account.$$resource.fetch(message.id, 'edit').then(function(data) {
Account.$log.debug('New message: ' + JSON.stringify(data, undefined, 2));
angular.extend(message, data);
message.$formatFullAddresses();
deferred.resolve(message);
}, function(data) {
deferred.reject(data);
});
}, function(data) {
deferred.reject(data);
});
return mailboxes;
return deferred.promise;
};
})();
@@ -75,13 +75,13 @@
* @memberof Mailbox
* @desc Fetch list of mailboxes of a specific account
* @param {string} accountId - the account
* @return a promise of the HTTP operation
* @see {@link Account.$getMailboxes}
*/
Mailbox.$find = function(account) {
var path, futureMailboxData;
path = Mailbox.$absolutePath(account.id);
futureMailboxData = this.$$resource.post(path, 'view', {sortingAttributes: {sort: 'date', asc: false}});
futureMailboxData = this.$$resource.post(account.id, 'view', {sortingAttributes: {sort: 'date', asc: false}});
return Mailbox.$unwrapCollection(account, futureMailboxData); // a collection of mailboxes
};
@@ -206,7 +206,17 @@
}
return loaded;
};
/**
* @function $deleteMessages
* @memberof Mailbox.prototype
* @desc Delete multiple messages from mailbox.
* @return a promise of the HTTP operation
*/
Mailbox.prototype.$deleteMessages = function(uids) {
return Mailbox.$$resource.post(this.id, 'batchDelete', {uids: uids});
};
/**
* @function $omit
* @memberof Mailbox.prototype
@@ -107,21 +107,50 @@
/**
* @function $content
* @memberof Message.prototype
* @desc Fetch the message body along with other metadata such as the list of attachments.
* @returns the HTML representation of the body or a promise of the HTTP operation
* @desc Get the message body as accepted by SCE (Angular Strict Contextual Escaping).
* @returns the HTML representation of the body
*/
Message.prototype.$content = function() {
var futureMessageData;
return Message.$sce.trustAs('html', this.content);
};
if (this.$futureMessageData) {
return Message.$sce.trustAs('html', this.content);
}
/**
* @function $update
* @memberof Message.prototype
* @desc Fetch the message body along with other metadata such as the list of attachments.
* @returns a promise of the HTTP operation
*/
Message.prototype.$update = function() {
var futureMessageData;
futureMessageData = Message.$$resource.fetch(this.id, 'view');
return this.$unwrap(futureMessageData);
};
Message.prototype.$save = function() {
var data = this.$omit();
Message.$log.debug(JSON.stringify(data, undefined, 2));
return Message.$$resource.save(this.$absolutePath({asDraft: true}), data);
};
Message.prototype.$send = function() {
var data = this.$omit(),
deferred = Message.$q.defer();
Message.$$resource.post(this.$absolutePath({asDraft: true}), 'send', data).then(function(data) {
if (data.status == 'success') {
deferred.resolve(data);
}
else {
deferred.reject(data);
}
});
return deferred.promise;
};
/**
* @function $unwrap
* @memberof Message.prototype
@@ -154,4 +183,28 @@
return deferred.promise;
};
/**
* @function $omit
* @memberof Message.prototype
* @desc Return a sanitized object used to send to the server.
* @return an object literal copy of the Message instance
*/
Message.prototype.$omit = function() {
var message = {};
angular.forEach(this, function(value, key) {
if (key != 'constructor' && key[0] != '$') {
message[key] = value;
}
});
// Format addresses as arrays
_.each(['from', 'to', 'cc', 'bcc', 'reply-to'], function(type) {
if (message[type])
message[type] = _.invoke(message[type].split(','), 'trim');
});
//Message.$log.debug(JSON.stringify(message, undefined, 2));
return message;
};
})();
+68 -18
View File
@@ -6,7 +6,7 @@
angular.module('SOGo.Common', []);
angular.module('SOGo.MailerUI', ['ngSanitize', 'ui.router', 'mm.foundation', 'vs-repeat', 'SOGo.Common', 'SOGo.UICommon', 'SOGo.UIDesktop'])
angular.module('SOGo.MailerUI', ['ngSanitize', 'ui.router', 'mm.foundation', 'vs-repeat', 'ck', 'SOGo.Common', 'SOGo.UICommon', 'SOGo.UIDesktop'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
@@ -34,9 +34,7 @@
var promises = [];
// Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) {
console.debug(i);
var mailboxes = account.$getMailboxes();
console.debug(mailboxes);
promises.push(mailboxes.then(function(objects) {
return account;
}));
@@ -90,10 +88,10 @@
}
})
.state('mail.account.mailbox.message', {
url: "/:messageId",
url: '/:messageId',
views: {
message: {
templateUrl: "message.html",
templateUrl: 'message.html',
controller: 'MessageCtrl'
}
},
@@ -102,25 +100,47 @@
var message = _.find(stateMessages, function(messageObject) {
return messageObject.uid == $stateParams.messageId;
});
return message;
return message.$update();
}]
}
})
.state('mail.account.mailbox.message.editMessage', {
url: '/edit',
views: {
'mailbox@mail': {
templateUrl: 'editorTemplate', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorCtrl'
}
}
})
.state('mail.newMessage', {
url: '/new',
views: {
mailbox: {
templateUrl: 'editorTemplate', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorCtrl'
}
},
resolve: {
stateMessage: ['stateAccounts', function(stateAccounts) {
if (stateAccounts.length > 0) {
return stateAccounts[0].$newMessage();
}
}]
}
});
// .state('mailbox.newMessage', {
// url: "/new",
// templateUrl: "messageEditor.html",
// controller: 'MessageCtrl'
// })
// .state('mailbox.editMessage', {
// url: "/:messageId/edit",
// templateUrl: "messageEditor.html",
// controller: 'MessageCtrl'
// });
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/Mail');
}])
.run(function($rootScope) {
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.log(event, current, previous, rejection)
})
})
.directive('sgFocusOn', function() {
return function(scope, elem, attr) {
scope.$on('sgFocusOn', function(e, name) {
@@ -166,7 +186,7 @@
});
};
if (_.isEmpty($state.params) && $scope.accounts.length > 0 && $scope.accounts[0].$mailboxes.length > 0) {
if ($state.current.name == 'mail' && $scope.accounts.length > 0 && $scope.accounts[0].$mailboxes.length > 0) {
var account = $scope.accounts[0];
var mailbox = account.$mailboxes[0];
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(mailbox.path) });
@@ -182,8 +202,38 @@
});
}])
.controller('MessageCtrl', ['$scope', '$rootScope', '$stateParams', 'stateMessage', '$timeout', '$modal', 'sgFocus', 'sgDialog', 'sgAccount', 'sgMailbox', function($scope, $rootScope, $stateParams, stateMessage, $timeout, $modal, focus, Dialog, Account, Mailbox) {
.controller('MessageCtrl', ['$scope', '$rootScope', '$stateParams', '$state', 'stateAccount', 'stateMailbox', 'stateMessage', '$timeout', '$modal', 'encodeUriFilter', 'sgFocus', 'sgDialog', 'sgAccount', 'sgMailbox', function($scope, $rootScope, $stateParams, $state, stateAccount, stateMailbox, stateMessage, $timeout, $modal, encodeUriFilter, focus, Dialog, Account, Mailbox) {
$rootScope.message = stateMessage;
$scope.doDelete = function() {
stateMailbox.$deleteMessages([stateMessage.uid]).then(function() {
// Remove card from list of addressbook
stateMailbox.$messages = _.reject(stateMailbox.$messages, function(o) {
return o.uid == stateMessage.uid;
});
// Remove card object from scope
$rootScope.message = null;
$state.go('mail.account.mailbox', { accountId: stateAccount.id, mailboxId: encodeUriFilter(stateMailbox.path) });
});
};
}])
.controller('MessageEditorCtrl', ['$scope', '$rootScope', '$stateParams', 'stateAccounts', 'stateMessage', '$timeout', '$modal', 'sgFocus', 'sgDialog', 'sgAccount', 'sgMailbox', function($scope, $rootScope, $stateParams, stateAccounts, stateMessage, $timeout, $modal, focus, Dialog, Account, Mailbox) {
if (angular.isDefined(stateMessage)) {
$scope.message = stateMessage;
// Flatten addresses as strings
_.each(['from', 'to', 'cc', 'bcc', 'reply-to'], function(type) {
if ($scope.message[type])
$scope.message[type] = _.pluck($scope.message[type], 'full').join(', ');
});
}
$scope.identities = _.flatten(_.pluck(stateAccounts, 'identities'));
$scope.send = function(message) {
message.$send().then(function(data) {
console.debug('success ' + JSON.stringify(data, undefined, 2));
}, function(data) {
console.debug('failure ' + JSON.stringify(data, undefined, 2));
});
};
}]);
})();
+66
View File
@@ -0,0 +1,66 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for CKEditor module */
(function() {
'use strict';
angular.module('ck', []).directive('ckEditor', function() {
var calledEarly, loaded;
loaded = false;
calledEarly = false;
return {
restrict: 'C',
require: '?ngModel',
compile: function(element, attributes, transclude) {
var loadIt, local;
local = this;
loadIt = function() {
return calledEarly = true;
};
element.ready(function() {
return loadIt();
});
return {
post: function($scope, element, attributes, controller) {
if (calledEarly) {
return local.link($scope, element, attributes, controller);
}
loadIt = (function($scope, element, attributes, controller) {
return function() {
local.link($scope, element, attributes, controller);
};
})($scope, element, attributes, controller);
}
};
},
link: function($scope, elm, attr, ngModel) {
var ck;
if (!ngModel) {
return;
}
if (calledEarly && !loaded) {
return loaded = true;
}
loaded = false;
ck = CKEDITOR.replace(elm[0]);
ck.on('pasteState', function() {
$scope.$apply(function() {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function(value) {
ck.setData(ngModel.$viewValue);
};
}
};
});
})();