(js) Review Mail module to decrease watchers

This commit is contained in:
Francis Lachapelle
2017-05-18 15:31:38 -04:00
parent 083be7e89f
commit 28ae2fd58a
13 changed files with 1017 additions and 524 deletions
+21
View File
@@ -9,6 +9,27 @@ String.prototype.startsWith = function(pattern, position) {
return this.lastIndexOf(pattern, position) === position;
};
// See ngSanitize
String.prototype.encodeEntities = function () {
// Regular Expressions for parsing tags and attributes
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g;
return this.
replace(/&/g, '&').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
};
String.prototype._base64_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
String.prototype.base64encode = function () {
var output = "";
@@ -44,6 +44,7 @@
selectedFolder: null,
$refreshTimeout: null,
$virtualMode: false,
$virtualPath: false,
PRELOAD: PRELOAD
});
// Initialize sort parameters from user's settings
@@ -476,6 +477,16 @@
return this.flags.indexOf('noselect') >= 0;
};
/**
* @function getClassName
* @memberof Mailbox.prototype
* @desc Not used but defined because it is called from UIxAclEditor.wox.
* @returns a string representing the foreground CSS class name
*/
Mailbox.prototype.getClassName = function(base) {
return false;
};
/**
* @function $rename
* @memberof AddressBook.prototype
@@ -623,7 +634,14 @@
* @returns a promise of the HTTP operation
*/
Mailbox.prototype.$markAsRead = function() {
return Mailbox.$$resource.post(this.id, 'markRead');
var _this = this;
return Mailbox.$$resource.post(this.id, 'markRead').then(function() {
_this.unseenCount = 0;
_.forEach(_this.$messages, function(message) {
message.isread = true;
});
});
};
/**
@@ -931,6 +949,7 @@
}, function(data) {
angular.extend(_this, data);
_this.isError = true;
_this.$isLoading = false;
deferred.reject();
});
@@ -12,85 +12,68 @@
defaultWindowTitle = angular.element($window.document).find('title').attr('sg-default') || "SOGo",
hotkeys = [];
// Expose controller for eventual popup windows
$window.$mailboxController = vm;
this.$onInit = function() {
// Expose controller for eventual popup windows
$window.$mailboxController = vm;
vm.service = Mailbox;
vm.accounts = stateAccounts;
vm.account = stateAccount;
vm.selectedFolder = stateMailbox;
vm.selectMessage = selectMessage;
vm.messageDialog = null; // also access from Message controller
vm.toggleMessageSelection = toggleMessageSelection;
vm.sort = sort;
vm.sortedBy = sortedBy;
vm.searchMode = searchMode;
vm.cancelSearch = cancelSearch;
vm.newMessage = newMessage;
vm.mode = { search: false, multiple: 0 };
vm.confirmDeleteSelectedMessages = confirmDeleteSelectedMessages;
vm.markOrUnMarkMessagesAsJunk = markOrUnMarkMessagesAsJunk;
vm.copySelectedMessages = copySelectedMessages;
vm.moveSelectedMessages = moveSelectedMessages;
vm.markSelectedMessagesAsFlagged = markSelectedMessagesAsFlagged;
vm.markSelectedMessagesAsUnread = markSelectedMessagesAsUnread;
vm.markSelectedMessagesAsRead = markSelectedMessagesAsRead;
vm.selectAll = selectAll;
vm.unselectMessages = unselectMessages;
this.service = Mailbox;
this.accounts = stateAccounts;
this.account = stateAccount;
this.selectedFolder = stateMailbox;
this.messageDialog = null; // also access from Message controller
this.mode = { search: false, multiple: 0 };
_registerHotkeys(hotkeys);
stateMailbox.selectFolder();
_registerHotkeys(hotkeys);
// Expunge mailbox when leaving the Mail module
angular.element($window).on('beforeunload', _compactBeforeUnload);
$scope.$on('$destroy', function() {
angular.element($window).off('beforeunload', _compactBeforeUnload);
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
// Expunge mailbox when leaving the Mail module
angular.element($window).on('beforeunload', _compactBeforeUnload);
$scope.$on('$destroy', function() {
angular.element($window).off('beforeunload', _compactBeforeUnload);
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
});
// Update window's title with unseen messages count of selected mailbox
$scope.$watch(function() { return vm.selectedFolder.unseenCount; }, function(unseenCount) {
var title = defaultWindowTitle + ' - ';
if (unseenCount)
title += '(' + unseenCount + ') ';
title += vm.selectedFolder.$displayName;
$window.document.title = title;
});
// Update window's title with unseen messages count of selected mailbox
$scope.$watch(function() { return vm.selectedFolder.unseenCount; }, function(unseenCount) {
var title = defaultWindowTitle + ' - ';
if (unseenCount)
title += '(' + unseenCount + ') ';
title += vm.selectedFolder.$displayName;
$window.document.title = title;
});
};
function _registerHotkeys(keys) {
keys.push(sgHotkeys.createHotkey({
key: l('hotkey_search'),
description: l('Search'),
callback: searchMode
callback: vm.searchMode
}));
keys.push(sgHotkeys.createHotkey({
key: l('hotkey_compose'),
description: l('Write a new message'),
callback: function($event) {
if (vm.messageDialog === null)
newMessage($event);
vm.newMessage($event);
}
}));
keys.push(sgHotkeys.createHotkey({
key: l('hotkey_junk'),
description: l('Mark the selected messages as junk'),
callback: markOrUnMarkMessagesAsJunk
callback: vm.markOrUnMarkMessagesAsJunk
}));
keys.push(sgHotkeys.createHotkey({
key: 'space',
description: l('Toggle item'),
callback: toggleMessageSelection
callback: vm.toggleMessageSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'shift+space',
description: l('Toggle range of items'),
callback: toggleMessageSelection
callback: vm.toggleMessageSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'up',
@@ -119,7 +102,7 @@
keys.push(sgHotkeys.createHotkey({
key: 'backspace',
description: l('Delete selected message or folder'),
callback: confirmDeleteSelectedMessages
callback: vm.confirmDeleteSelectedMessages
}));
// Register the hotkeys
@@ -132,20 +115,20 @@
return vm.selectedFolder.$compact();
}
function sort(field) {
this.sort = function(field) {
vm.selectedFolder.$filter({ sort: field });
}
};
function sortedBy(field) {
this.sortedBy = function(field) {
return Mailbox.$query.sort == field;
}
};
function searchMode() {
this.searchMode = function() {
vm.mode.search = true;
focus('search');
}
};
function cancelSearch() {
this.cancelSearch = function() {
vm.mode.search = false;
vm.selectedFolder.$filter().then(function() {
if (vm.selectedFolder.selectedMessage) {
@@ -154,9 +137,9 @@
});
}
});
}
};
function newMessage($event, inPopup) {
this.newMessage = function($event, inPopup) {
var message;
if (vm.messageDialog === null) {
@@ -183,7 +166,7 @@
});
}
}
}
};
function _newMessageInPopup() {
var url = [sgSettings.baseURL(),
@@ -227,7 +210,7 @@
}
if (index > -1)
selectMessage(vm.selectedFolder.$messages[index]);
vm.selectMessage(vm.selectedFolder.$messages[index]);
$event.preventDefault();
@@ -250,7 +233,7 @@
index = 0;
if (index < vm.selectedFolder.getLength())
selectMessage(vm.selectedFolder.$messages[index]);
vm.selectMessage(vm.selectedFolder.$messages[index]);
else
index = -1;
@@ -265,7 +248,7 @@
if (vm.selectedFolder.hasSelectedMessage()) {
index = _nextMessage($event);
if (index >= 0)
toggleMessageSelection($event, vm.selectedFolder.$messages[index]);
vm.toggleMessageSelection($event, vm.selectedFolder.$messages[index]);
}
}
@@ -275,23 +258,25 @@
if (vm.selectedFolder.hasSelectedMessage()) {
index = _previousMessage($event);
if (index >= 0)
toggleMessageSelection($event, vm.selectedFolder.$messages[index]);
vm.toggleMessageSelection($event, vm.selectedFolder.$messages[index]);
}
}
function selectMessage(message) {
this.selectMessage = function(message) {
if (Mailbox.$virtualMode)
$state.go('mail.account.virtualMailbox.message', {mailboxId: encodeUriFilter(message.$mailbox.path), messageId: message.uid});
else
$state.go('mail.account.mailbox.message', {messageId: message.uid});
}
};
function toggleMessageSelection($event, message) {
this.toggleMessageSelection = function($event, message) {
var folder = vm.selectedFolder,
selectedIndex, nextSelectedIndex, i;
if (!message)
message = folder.$selectedMessage();
if (!message)
return true;
message.selected = !message.selected;
vm.mode.multiple += message.selected? 1 : -1;
@@ -320,7 +305,7 @@
$event.preventDefault();
$event.stopPropagation();
}
};
/**
* Batch operations
@@ -372,7 +357,7 @@
}
}
function confirmDeleteSelectedMessages($event) {
this.confirmDeleteSelectedMessages = function($event) {
var selectedMessages = vm.selectedFolder.$selectedMessages();
if (vm.messageDialog === null && _.size(selectedMessages) > 0)
@@ -417,9 +402,9 @@
});
$event.preventDefault();
}
};
function markOrUnMarkMessagesAsJunk() {
this.markOrUnMarkMessagesAsJunk = function() {
var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage();
var selectedMessages = vm.selectedFolder.$selectedMessages();
if (_.size(selectedMessages) === 0 && moveSelectedMessage)
@@ -443,9 +428,9 @@
}
});
});
}
};
function copySelectedMessages(dstFolder) {
this.copySelectedMessages = function(dstFolder) {
var selectedMessages = vm.selectedFolder.$selectedMessages();
if (_.size(selectedMessages) > 0)
vm.selectedFolder.$copyMessages(selectedMessages, '/' + dstFolder).then(function() {
@@ -455,9 +440,9 @@
.position('top right')
.hideDelay(2000));
});
}
};
function moveSelectedMessages(dstFolder) {
this.moveSelectedMessages = function(dstFolder) {
var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage();
var selectedMessages = vm.selectedFolder.$selectedMessages();
var count = vm.selectedFolder.$selectedCount();
@@ -479,9 +464,9 @@
_unselectMessage(moveSelectedMessage, index);
}
});
}
};
function selectAll() {
this.selectAll = function() {
var count = 0;
_.forEach(_currentMailboxes(), function(folder) {
var i = 0, length = folder.$messages.length;
@@ -490,18 +475,18 @@
count += length;
});
vm.mode.multiple = count;
}
};
function unselectMessages() {
this.unselectMessages = function() {
_.forEach(_currentMailboxes(), function(folder) {
_.forEach(folder.$messages, function(message) {
message.selected = false;
});
});
vm.mode.multiple = 0;
}
};
function markSelectedMessagesAsFlagged() {
this.markSelectedMessagesAsFlagged = function() {
var selectedMessages = vm.selectedFolder.$selectedMessages();
if (_.size(selectedMessages) > 0)
vm.selectedFolder.$flagMessages(selectedMessages, '\\Flagged', 'add').then(function(messages) {
@@ -509,9 +494,9 @@
message.isflagged = true;
});
});
}
};
function markSelectedMessagesAsUnread() {
this.markSelectedMessagesAsUnread = function() {
var selectedMessages = vm.selectedFolder.$selectedMessages();
if (_.size(selectedMessages) > 0) {
vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'remove').then(function(messages) {
@@ -522,9 +507,9 @@
});
});
}
}
};
function markSelectedMessagesAsRead() {
this.markSelectedMessagesAsRead = function() {
var selectedMessages = vm.selectedFolder.$selectedMessages();
if (_.size(selectedMessages) > 0) {
vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'add').then(function(messages) {
@@ -535,13 +520,13 @@
});
});
}
}
};
}
angular
.module('material.components.virtualRepeat')
.decorator('mdVirtualRepeatContainerDirective', mdVirtualRepeatContainerDirectiveDecorator);
.module('SOGo.MailerUI')
.controller('MailboxController', MailboxController);
/**
* @ngInject
@@ -560,7 +545,8 @@
}
angular
.module('SOGo.MailerUI')
.controller('MailboxController', MailboxController);
.module('material.components.virtualRepeat')
.decorator('mdVirtualRepeatContainerDirective', mdVirtualRepeatContainerDirectiveDecorator);
})();
@@ -6,70 +6,47 @@
/**
* @ngInject
*/
MailboxesController.$inject = ['$scope', '$state', '$timeout', '$window', '$mdDialog', '$mdToast', '$mdMedia', '$mdSidenav', 'sgConstant', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'sgHotkeys', 'Account', 'Mailbox', 'VirtualMailbox', 'User', 'Preferences', 'stateAccounts'];
function MailboxesController($scope, $state, $timeout, $window, $mdDialog, $mdToast, $mdMedia, $mdSidenav, sgConstant, focus, encodeUriFilter, Dialog, Settings, sgHotkeys, Account, Mailbox, VirtualMailbox, User, Preferences, stateAccounts) {
MailboxesController.$inject = ['$scope', '$state', '$transitions', '$timeout', '$window', '$mdDialog', '$mdToast', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'sgHotkeys', 'Account', 'Mailbox', 'VirtualMailbox', 'User', 'Preferences', 'stateAccounts'];
function MailboxesController($scope, $state, $transitions, $timeout, $window, $mdDialog, $mdToast, focus, encodeUriFilter, Dialog, Settings, sgHotkeys, Account, Mailbox, VirtualMailbox, User, Preferences, stateAccounts) {
var vm = this,
account,
mailbox,
hotkeys = [];
vm.service = Mailbox;
vm.accounts = stateAccounts;
vm.toggleAccountState = toggleAccountState;
vm.subscribe = subscribe;
vm.newFolder = newFolder;
vm.delegate = delegate;
vm.editFolder = editFolder;
vm.revertEditing = revertEditing;
vm.selectFolder = selectFolder;
vm.saveFolder = saveFolder;
vm.compactFolder = compactFolder;
vm.emptyTrashFolder = emptyTrashFolder;
vm.confirmDelete = confirmDelete;
vm.markFolderRead = markFolderRead;
vm.share = share;
vm.setFolderAs = setFolderAs;
vm.refreshUnseenCount = refreshUnseenCount;
vm.isDroppableFolder = isDroppableFolder;
vm.dragSelectedMessages = dragSelectedMessages;
this.$onInit = function () {
this.service = Mailbox;
this.accounts = stateAccounts;
// Advanced search options
vm.showingAdvancedSearch = false;
vm.currentSearchParam = '';
vm.addSearchParam = addSearchParam;
vm.newSearchParam = newSearchParam;
vm.showAdvancedSearch = showAdvancedSearch;
vm.hideAdvancedSearch = hideAdvancedSearch;
vm.toggleAdvancedSearch = toggleAdvancedSearch;
vm.search = {
options: {'': '', // no placeholder when no criteria is active
subject: l('Enter Subject'),
from: l('Enter From'),
to: l('Enter To'),
cc: l('Enter Cc'),
body: l('Enter Body')
},
mailbox: 'INBOX',
subfolders: 1,
match: 'AND',
params: []
};
// Advanced search options
this.currentSearchParam = '';
this.search = {
options: {'': '', // no placeholder when no criteria is active
subject: l('Enter Subject'),
from: l('Enter From'),
to: l('Enter To'),
cc: l('Enter Cc'),
body: l('Enter Body')
},
subfolders: 1,
match: 'AND',
params: []
};
Preferences.ready().then(function() {
vm.showSubscribedOnly = Preferences.defaults.SOGoMailShowSubscribedFoldersOnly;
});
vm.refreshUnseenCount();
_registerHotkeys(hotkeys);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
Preferences.ready().then(function() {
vm.showSubscribedOnly = Preferences.defaults.SOGoMailShowSubscribedFoldersOnly;
});
});
this.refreshUnseenCount();
_registerHotkeys(hotkeys);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
};
function _registerHotkeys(keys) {
@@ -88,24 +65,16 @@
});
}
function showAdvancedSearch(path) {
vm.showingAdvancedSearch = true;
vm.search.mailbox = path;
// Close sidenav on small devices
if (!$mdMedia(sgConstant['gt-md']))
$mdSidenav('left').close();
}
function hideAdvancedSearch() {
vm.showingAdvancedSearch = false;
this.hideAdvancedSearch = function() {
vm.service.$virtualPath = false;
vm.service.$virtualMode = false;
account = vm.accounts[0];
mailbox = vm.searchPreviousMailbox;
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(mailbox.path) });
}
};
function toggleAdvancedSearch() {
this.toggleAdvancedSearch = function() {
if (Mailbox.selectedFolder.$isLoading) {
// Stop search
vm.virtualMailbox.stopSearch();
@@ -133,8 +102,8 @@
Mailbox.selectedFolder = vm.virtualMailbox;
Mailbox.$virtualMode = true;
if (angular.isDefined(vm.search.mailbox)) {
root = vm.accounts[0].$getMailboxByPath(vm.search.mailbox);
if (angular.isDefined(Mailbox.$virtualPath)) {
root = vm.accounts[0].$getMailboxByPath(Mailbox.$virtualPath);
mailboxes.push(root);
if (vm.search.subfolders && root.children.length)
_visit(root.children);
@@ -145,17 +114,18 @@
vm.virtualMailbox.setMailboxes(mailboxes);
vm.virtualMailbox.startSearch(vm.search.match, vm.search.params);
$state.go('mail.account.virtualMailbox', { accountId: vm.accounts[0].id });
if ($state.$current.name != 'mail.account.virtualMailbox')
$state.go('mail.account.virtualMailbox', { accountId: vm.accounts[0].id });
}
}
};
function addSearchParam(v) {
this.addSearchParam = function(v) {
vm.currentSearchParam = v;
focus('advancedSearch');
return false;
}
};
function newSearchParam(pattern) {
this.newSearchParam = function(pattern) {
if (pattern.length && vm.currentSearchParam.length) {
var n = 0, searchParam = vm.currentSearchParam;
if (pattern.startsWith("!")) {
@@ -165,9 +135,9 @@
vm.currentSearchParam = '';
return { searchBy: searchParam, searchInput: pattern, negative: n };
}
}
};
function toggleAccountState(account) {
this.toggleAccountState = function (account) {
account.$expanded = !account.$expanded;
account.$flattenMailboxes({ reload: true, saveState: true });
// Fire a window resize to recompute the virtual-repeater.
@@ -176,9 +146,9 @@
$timeout(function() {
angular.element($window).triggerHandler('resize');
}, 150);
}
};
function subscribe(account) {
this.subscribe = function(account) {
$mdDialog.show({
templateUrl: account.id + '/subscribe',
controller: SubscriptionsDialogController,
@@ -212,12 +182,12 @@
});
function close() {
$mdDialog.cancel();
$mdDialog.hide();
}
}
}
};
function newFolder(parentFolder) {
this.newFolder = function(parentFolder) {
Dialog.prompt(l('New Folder...'),
l('Enter the new name of your folder'))
.then(function(name) {
@@ -229,9 +199,9 @@
l(data.error));
});
});
}
};
function delegate(account) {
this.delegate = function(account) {
$mdDialog.show({
templateUrl: account.id + '/delegation', // UI/Templates/MailerUI/UIxMailUserDelegation.wox
controller: MailboxDelegationController,
@@ -285,114 +255,9 @@
}
}
}
} // delegate
}; // delegate
function editFolder(folder) {
vm.editMode = folder.path;
focus('mailboxName_' + folder.path);
}
function revertEditing(folder) {
folder.$reset();
vm.editMode = false;
}
function selectFolder($event, account, folder) {
if (vm.editMode == folder.path)
return;
vm.editMode = false;
vm.showingAdvancedSearch = false;
vm.service.$virtualMode = false;
// Close sidenav on small devices
if (!$mdMedia(sgConstant['gt-md']))
$mdSidenav('left').close();
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(folder.path) });
$event.stopPropagation();
$event.preventDefault();
}
function saveFolder(folder) {
folder.$rename()
.then(function(data) {
vm.editMode = false;
});
}
function compactFolder(folder) {
folder.$compact().then(function() {
$mdToast.show(
$mdToast.simple()
.content(l('Folder compacted'))
.position('top right')
.hideDelay(3000));
});
}
function emptyTrashFolder(folder) {
folder.$emptyTrash().then(function() {
$mdToast.show(
$mdToast.simple()
.content(l('Trash emptied'))
.position('top right')
.hideDelay(3000));
});
}
function confirmDelete(folder) {
Dialog.confirm(l('Warning'),
l('Do you really want to move this folder into the trash ?'),
{ ok: l('Delete') })
.then(function() {
folder.$delete()
.then(function() {
$state.go('mail.account.inbox');
}, function(response) {
Dialog.confirm(l('Warning'),
l('The mailbox could not be moved to the trash folder. Would you like to delete it immediately?'),
{ ok: l('Delete') })
.then(function() {
folder.$delete({ withoutTrash: true })
.then(function() {
$state.go('mail.account.inbox');
}, function(response) {
Dialog.alert(l('An error occured while deleting the mailbox "%{0}".', folder.name),
l(response.error));
});
});
});
});
}
function markFolderRead(folder) {
folder.$markAsRead();
}
function share(folder) {
// Fetch list of ACL users
folder.$acl.$users().then(function() {
// Show ACL editor
$mdDialog.show({
templateUrl: folder.id + '/UIxAclEditor', // UI/Templates/UIxAclEditor.wox
controller: 'AclController', // from the ng module SOGo.Common
controllerAs: 'acl',
clickOutsideToClose: true,
escapeToClose: true,
locals: {
usersWithACL: folder.$acl.users,
User: User,
folder: folder
}
});
});
} // share
function setFolderAs(folder, type) {
folder.$setFolderAs(type).then(function() {
folder.$account.$getMailboxes({reload: true});
});
}
function refreshUnseenCount() {
this.refreshUnseenCount = function() {
var unseenCountFolders = $window.unseenCountFolders;
_.forEach(vm.accounts, function(account) {
@@ -422,13 +287,13 @@
if (refreshViewCheck && refreshViewCheck != 'manually')
$timeout(vm.refreshUnseenCount, refreshViewCheck.timeInterval()*1000);
});
}
};
function isDroppableFolder(srcFolder, dstFolder) {
this.isDroppableFolder = function(srcFolder, dstFolder) {
return (dstFolder.id != srcFolder.id) && !dstFolder.isNoSelect();
}
};
function dragSelectedMessages(srcFolder, dstFolder, mode) {
this.dragSelectedMessages = function(srcFolder, dstFolder, mode) {
var dstId, messages, uids, clearMessageView, promise, success;
dstId = '/' + dstFolder.id;
@@ -456,7 +321,7 @@
.position('top right')
.hideDelay(2000));
});
}
};
}
+45 -31
View File
@@ -206,35 +206,53 @@
if (mailbox) {
mailbox.$topIndex = 0;
mailbox.selectFolder();
return mailbox;
}
else
// Mailbox not found
return $state.go('mail.account.inbox');
return $q.reject("Mailbox doesn't exist");
}
/**
* @ngInject
*/
onEnterInbox.$inject = ['$window', '$state', 'encodeUriFilter', 'stateAccount'];
function onEnterInbox($window, $state, encodeUriFilter, stateAccount) {
if (stateAccount.$mailboxes.length > 0)
$window.location.hash = $state.href('mail.account.mailbox',
{accountId: stateAccount.id,
mailboxId: encodeUriFilter(stateAccount.$mailboxes[0].path)});
else
$state.go('mail');
onEnterInbox.$inject = ['$transition$', 'encodeUriFilter', 'Mailbox'];
function onEnterInbox($transition, encodeUriFilter, Mailbox) {
var stateAccountPromise = $transition.injector().getAsync('stateAccount');
return stateAccountPromise.then(function(stateAccount) {
if (stateAccount.$mailboxes.length > 0) {
return $transition.router.stateService.target('mail.account.mailbox', {
accountId: stateAccount.id,
mailboxId: encodeUriFilter(stateAccount.$mailboxes[0].path)
});
}
else {
Mailbox.selectedFolder = false;
return $transition.router.stateService.target('mail');
}
});
}
/**
* @ngInject
*/
stateMessages.$inject = ['Mailbox', 'stateMailbox'];
function stateMessages(Mailbox, stateMailbox) {
stateMessages.$inject = ['$q', '$state', 'Mailbox', 'stateMailbox'];
function stateMessages($q, $state, Mailbox, stateMailbox) {
var promise;
if (Mailbox.$virtualMode)
return [];
return stateMailbox.$filter();
if (stateMailbox)
promise = stateMailbox.$filter().catch(function() {
// Mailbox not found
return $q.reject('Mailbox not found');
});
else
promise = $q.reject("Mailbox doesn't exist");
return promise;
}
/**
@@ -314,25 +332,21 @@
/**
* @ngInject
*/
// stateContent.$inject = ['stateMessage'];
// function stateContent(stateMessage) {
// return stateMessage.$editableContent();
// }
/**
* @ngInject
*/
runBlock.$inject = ['$rootScope', '$log', '$state', 'Mailbox'];
function runBlock($rootScope, $log, $state, Mailbox) {
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
$log.error(error);
event.preventDefault();
// Unselect everything
Mailbox.selectedFolder = false;
$state.go('mail');
});
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
$log.error(event, current, previous, rejection);
runBlock.$inject = ['$window', '$transitions', '$log', '$state', 'Mailbox'];
function runBlock($window, $transitions, $log, $state, Mailbox) {
if (!$window.DebugEnabled)
$state.defaultErrorHandler(function() {
// Don't report any state error
});
$transitions.onError({ to: 'mail.**' }, function(transition) {
if (transition.to().name != 'mail' &&
!transition.ignored() &&
transition.error().message.indexOf('superseded') < 0) {
$log.error('transition error to ' + transition.to().name);
// Unselect everything
Mailbox.selectedFolder = false;
$state.go('mail');
}
});
}
@@ -0,0 +1,58 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
/**
* sgAccountSection - A directive that is only a controller to manage the selection of the mailboxes.
* @memberof SOGo.MailerUI
*/
function sgAccountSection() {
return {
restrict: 'C',
scope: {},
controller: 'sgAccountController'
};
}
/**
* @ngInject
*/
sgAccountController.$inject = ['$element', '$transitions', '$state', '$mdMedia', '$mdSidenav', 'sgConstant', 'Mailbox', 'encodeUriFilter'];
function sgAccountController($element, $transitions, $state, $mdMedia, $mdSidenav, sgConstant, Mailbox, encodeUriFilter) {
var $ctrl = this, mailboxes = [];
this.$postLink = function () {
this.quotaElement = _.find($element.find('div'), function(div) {
return div.classList.contains('sg-quota');
});
};
// Register a sgMailboxListItem controller
this.addMailboxController = function (mailboxController) {
mailboxes.push(mailboxController);
};
// Called from a sgMailboxListItem controller
this.selectFolder = function (mailboxController) {
if (Mailbox.selectedFolder !== null) {
var selectedMailboxCtrl = _.find(mailboxes, function(ctrl) {
return ctrl.mailbox.id == Mailbox.selectedFolder.id;
});
if (selectedMailboxCtrl)
selectedMailboxCtrl.unselectFolder();
}
// Close sidenav on small devices
if (!$mdMedia(sgConstant['gt-md']))
$mdSidenav('left').close();
};
}
angular
.module('SOGo.MailerUI')
.controller('sgAccountController', sgAccountController)
.directive('sgAccountSection', sgAccountSection);
})();
@@ -0,0 +1,302 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
/**
* sgMailboxListItem - A directive that defines the content of a md-list-item for a mailbox.
* @memberof SOGo.MailerUI
*/
function sgMailboxListItem() {
return {
restrict: 'C',
require: {
accountController: '^^sgAccountSection'
},
scope: {},
bindToController: {
mailbox: '=sgMailbox'
},
template: [
' <div class="sg-child-level-0"',
' ng-class="::$ctrl.childLevel()">',
' <md-checkbox class="sg-folder"',
' ng-class="::$ctrl.mailbox.$icon"',
' aria-label="' + l("Expanded") + '"',
' ng-model="$ctrl.mailbox.$expanded"',
' ng-disabled="$ctrl.mailbox.children.length == 0"',
' ng-change="$ctrl.mailbox.$account.$flattenMailboxes({ reload: true, saveState: true })">',
' <md-icon>{{::$ctrl.mailbox.$icon}}</md-icon></md-checkbox>',
' </div>',
' <p class="sg-item-name"',
' ng-click="$ctrl.selectFolder($event)"',
' ng-dblclick="$ctrl.editFolder($event)">',
' <span ng-bind="$ctrl.mailbox.$displayName"></span>',
' <span class="sg-counter-badge ng-hide"',
' ng-show="$ctrl.mailbox.unseenCount"',
' ng-bind="$ctrl.mailbox.unseenCount"></span>',
' </p>',
' <md-input-container class="md-flex ng-hide">',
' <input class="sg-item-name" type="text"',
' aria-label="' + l("Enter the new name of your folder") + '"',
' ng-blur="$ctrl.saveFolder($event)"',
' sg-enter="$ctrl.saveFolder($event)"',
' sg-escape="$ctrl.revertEditing()" />',
' </md-input-container>',
' <md-icon class="md-menu" ng-click="$ctrl.showMenu($event)" aria-label="' + l("Options") + '">more_vert</md-icon>'
].join(''),
controller: 'sgMailboxListItemController',
controllerAs: '$ctrl'
};
}
/**
* @ngInject
*/
sgMailboxListItemController.$inject = ['$scope', '$element', '$compile', '$state', '$mdToast', '$mdPanel', '$mdMedia', '$mdSidenav', 'sgConstant', 'Dialog', 'Mailbox', 'encodeUriFilter'];
function sgMailboxListItemController($scope, $element, $compile, $state, $mdToast, $mdPanel, $mdMedia, $mdSidenav, sgConstant, Dialog, Mailbox, encodeUriFilter) {
var $ctrl = this;
this.$onInit = function() {
this.$element = $element;
this.service = Mailbox;
this.editMode = false;
this.accountController.addMailboxController(this);
};
this.$postLink = function() {
this.selectableElement = $element.find('div')[0];
this.clickableElement = $element.find('p')[0];
this.inputContainer = $element.find('md-input-container')[0];
this.inputElement = $element.find('input')[0];
this.moreOptionsButton = _.last($element.find('md-icon'));
// Check if router's state has selected a mailbox
if (Mailbox.selectedFolder !== null && Mailbox.selectedFolder.id == this.mailbox.id) {
this.selectFolder();
}
};
this.childLevel = function() {
return 'sg-child-level-' + this.mailbox.level;
};
this.selectFolder = function($event) {
if (this.editMode || this.mailbox == Mailbox.selectedFolder)
return;
Mailbox.$virtualPath = false;
Mailbox.$virtualMode = false;
this.accountController.selectFolder(this);
if ($event) {
$state.go('mail.account.mailbox', {
accountId: this.mailbox.$account.id,
mailboxId: encodeUriFilter(this.mailbox.path)
});
$event.stopPropagation();
$event.preventDefault();
}
};
this.unselectFolder = function() {
$element[0].classList.remove('md-bg');
};
this.editFolder = function($event) {
this.editMode = true;
this.inputElement.value = this.mailbox.name;
this.clickableElement.classList.add('ng-hide');
this.inputContainer.classList.remove('ng-hide');
this.inputElement.focus();
this.inputElement.select();
if ($event) {
$event.stopPropagation();
$event.preventDefault();
}
};
this.saveFolder = function($event) {
if (this.inputElement.disabled)
return;
this.mailbox.name = this.inputElement.value;
this.inputElement.disabled = true;
this.mailbox.$rename()
.then(function(data) {
$ctrl.editMode = false;
$ctrl.inputContainer.classList.add('ng-hide');
$ctrl.clickableElement.classList.remove('ng-hide');
})
.finally(function() {
$ctrl.inputElement.disabled = false;
});
};
this.revertEditing = function() {
this.editMode = false;
this.clickableElement.classList.remove('ng-hide');
this.inputContainer.classList.add('ng-hide');
this.inputElement.value = this.mailbox.name;
};
this.showMenu = function($event) {
var panelPosition = $mdPanel.newPanelPosition()
.relativeTo(this.moreOptionsButton)
.addPanelPosition(
$mdPanel.xPosition.ALIGN_START,
$mdPanel.yPosition.ALIGN_TOPS
);
var panelAnimation = $mdPanel.newPanelAnimation()
.openFrom(this.moreOptionsButton)
.duration(100)
.withAnimation($mdPanel.animation.FADE);
var config = {
attachTo: angular.element(document.body),
locals: {
itemCtrl: this,
folder: this.mailbox
},
bindToController: true,
controller: MenuController,
controllerAs: '$menuCtrl',
position: panelPosition,
animation: panelAnimation,
targetEvent: $event,
templateUrl: 'UIxMailFolderMenu',
trapFocus: true,
clickOutsideToClose: true,
escapeToClose: true,
focusOnOpen: true
};
$mdPanel.open(config)
.then(function(panelRef) {
// Automatically close panel when clicking inside of it
panelRef.panelEl.one('click', function() {
panelRef.close();
});
});
MenuController.$inject = ['mdPanelRef', '$state', '$mdDialog', 'User'];
function MenuController(mdPanelRef, $state, $mdDialog, User) {
var $menuCtrl = this;
this.markFolderRead = function() {
this.folder.$markAsRead();
};
this.newFolder = function() {
Dialog.prompt(l('New Folder...'),
l('Enter the new name of your folder'))
.then(function(name) {
$menuCtrl.folder.$newMailbox($menuCtrl.folder.id, name)
.then(function() {
// success
}, function(data, status) {
Dialog.alert(l('An error occured while creating the mailbox "%{0}".', name),
l(data.error));
});
});
};
this.editFolder = function() {
this.itemCtrl.editFolder();
};
this.compactFolder = function() {
this.folder.$compact().then(function() {
$mdToast.show(
$mdToast.simple()
.content(l('Folder compacted'))
.position('top right')
.hideDelay(3000));
});
};
this.emptyTrashFolder = function() {
this.folder.$emptyTrash().then(function() {
$mdToast.show(
$mdToast.simple()
.content(l('Trash emptied'))
.position('top right')
.hideDelay(3000));
});
};
this.confirmDelete = function() {
Dialog.confirm(l('Warning'),
l('Do you really want to move this folder into the trash ?'),
{ ok: l('Delete') })
.then(function() {
$menuCtrl.folder.$delete()
.then(function() {
$state.go('mail.account.inbox');
}, function(response) {
Dialog.confirm(l('Warning'),
l('The mailbox could not be moved to the trash folder. Would you like to delete it immediately?'),
{ ok: l('Delete') })
.then(function() {
$menuCtrl.folder.$delete({ withoutTrash: true })
.then(function() {
$state.go('mail.account.inbox');
}, function(response) {
Dialog.alert(l('An error occured while deleting the mailbox "%{0}".', $menuCtrl.folder.name),
l(response.error));
});
});
});
});
};
this.showAdvancedSearch = function() {
Mailbox.$virtualPath = this.folder.path;
// Close sidenav on small devices
if (!$mdMedia(sgConstant['gt-md']))
$mdSidenav('left').close();
};
this.share = function() {
// Fetch list of ACL users
this.folder.$acl.$users().then(function() {
// Show ACL editor
$mdDialog.show({
templateUrl: $menuCtrl.folder.id + '/UIxAclEditor', // UI/Templates/UIxAclEditor.wox
controller: 'AclController', // from the ng module SOGo.Common
controllerAs: 'acl',
clickOutsideToClose: true,
escapeToClose: true,
locals: {
usersWithACL: $menuCtrl.folder.$acl.users,
User: User,
folder: $menuCtrl.folder
}
});
});
};
this.setFolderAs = function(type) {
this.folder.$setFolderAs(type).then(function() {
$menuCtrl.folder.$account.$getMailboxes({reload: true});
});
};
} // MenuController
};
}
angular
.module('SOGo.MailerUI')
.controller('sgMailboxListItemController', sgMailboxListItemController)
.directive('sgMailboxListItem', sgMailboxListItem);
})();
@@ -0,0 +1,77 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
/**
* sgMessageListItem - A directive that watches some attributes of a message. Any component inside the
* list item should depends on this directive and extend the 'onUpdate' method instead of creating new
* independent watchers.
* @memberof SOGo.MailerUI
*/
function sgMessageListItem() {
return {
restrict: 'C',
scope: {},
bindToController: {
message: '=sgMessage'
},
controller: 'sgMessageListItemController'
};
}
/**
* @ngInject
*/
sgMessageListItemController.$inject = ['$scope', '$element', 'Mailbox'];
function sgMessageListItemController($scope, $element, Mailbox) {
var $ctrl = this;
this.$onInit = function () {
// this.service = Message;
this.MailboxService = Mailbox;
$scope.$watch(
function() {
return $ctrl.message? [ _.pick($ctrl.message, ['uid', 'isread', 'isflagged']) ] : null;
},
function(newId, oldId) {
if ($ctrl.message) {
// Message has changed
$ctrl.onUpdate();
}
},
true // compare for object equality
);
};
this.onUpdate = function () {
// Is the message unread?
if (this.message.isread)
$element.removeClass('unread');
else
$element.addClass('unread');
// Is the message selected?
if (Mailbox.selectedFolder.isSelectedMessage(this.message.uid, this.message.$mailbox.path))
$element.addClass('md-default-theme md-accent md-bg md-hue-2');
else
$element.removeClass('md-default-theme md-accent md-bg md-hue-2');
};
this.setVisibility = function (element, visible) {
if (visible)
element.classList.remove('ng-hide');
else
element.classList.add('ng-hide');
};
}
angular
.module('SOGo.MailerUI')
.controller('sgMessageListItemController', sgMessageListItemController)
.directive('sgMessageListItem', sgMessageListItem);
})();
@@ -0,0 +1,181 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
/**
* sgMessageListItemMain - The main section of a list item for a message. It relies on the
* 'onUpdate' method of the parent sgMessageListItem controller to update its content.
* @memberof SOGo.MailerUI
* @example:
*/
function sgMessageListItemMain() {
return {
restrict: 'C',
require: '^^sgMessageListItem',
scope: {},
template: [
'<div class="sg-category"',
' ng-repeat="tag in $ctrl.message.flags | limitTo:5"',
' ng-style="{ \'background-color\': $ctrl.service.$tags[tag][1], left: ($index * 3) + \'px\' }"><!-- tags --></div>',
'<div class="sg-tile-content">',
' <div class="sg-md-subhead">',
' <div>',
' <span class="sg-label-outline ng-hide"><!-- mailbox --></span>',
' <md-icon class="ng-hide">error</md-icon>', // the priority icon
' <span><!-- sender or recipient --></span>',
' </div>',
' <div class="sg-tile-date"><!-- date --></div>',
' </div>',
' <div class="sg-md-body">',
' <div class="sg-tile-subject"><!-- subject --></div>',
' <div class="sg-tile-size"><!-- size --></div>',
' </div>',
'</div>',
'<div class="sg-tile-icons">',
' <md-icon class="ng-hide">star</md-icon>',
' <md-icon class="ng-hide">reply</md-icon>',
' <md-icon class="ng-hide">forward</md-icon>',
' <md-icon class="ng-hide">attach_file</md-icon>',
'</div>',
'<div class="sg-progress-linear-bottom">',
' <md-progress-linear class="md-accent"',
' md-mode="indeterminate"',
' ng-disabled="!$ctrl.message.$isLoading()"><!-- message loading progress --></md-progress-linear>',
'</div>'
].join(''),
link: postLink,
controller: 'sgMessageListItemMainController',
controllerAs: '$ctrl'
};
function postLink(scope, element, attrs, parentController) {
scope.parentController = parentController;
}
}
/**
* @ngInject
*/
sgMessageListItemMainController.$inject = ['$scope', '$element', '$parse', '$state', '$mdToast', 'Mailbox', 'Message', 'encodeUriFilter'];
function sgMessageListItemMainController($scope, $element, $parse, $state, $mdToast, Mailbox, Message, encodeUriFilter) {
var $ctrl = this;
this.$postLink = function () {
var contentDivElement, iconsDivElement;
var parentControllerOnUpdate, setVisibility;
this.parentController = $scope.parentController;
parentControllerOnUpdate = this.parentController.onUpdate;
setVisibility = this.parentController.setVisibility;
_.forEach($element.find('div'), function(div) {
if (div.classList.contains('sg-tile-content'))
contentDivElement = angular.element(div);
else if (div.classList.contains('sg-tile-icons'))
iconsDivElement = angular.element(div);
});
this.priorityIconElement = contentDivElement.find('md-icon')[0];
if (Mailbox.$virtualMode) {
// Show mailbox name in front of the subject
this.mailboxNameElement = contentDivElement.find('span')[0];
this.mailboxNameElement.classList.remove('ng-hide');
}
this.senderElement = contentDivElement.find('span')[1];
_.forEach(contentDivElement.find('div'), function(div) {
if (div.classList.contains('sg-tile-subject'))
$ctrl.subjectElement = div;
else if (div.classList.contains('sg-tile-size'))
$ctrl.sizeElement = div;
else if (div.classList.contains('sg-tile-date'))
$ctrl.dateElement = div;
});
_.forEach(iconsDivElement.find('md-icon'), function(div) {
if (div.textContent == 'star')
$ctrl.flagIconElement = div;
else if (div.textContent == 'reply')
$ctrl.answerIconElement = div;
else if (div.textContent == 'forward')
$ctrl.forwardIconElement = div;
else if (div.textContent == 'attach_file')
$ctrl.attachmentIconElement = div;
});
/**
* Update the template when the parent controller has detected a change.
*/
this.parentController.onUpdate = function () {
var i;
$ctrl.message = $ctrl.parentController.message;
console.debug('onUpdate ' + $ctrl.message.uid);
// Flags
for (i = 0; i < $ctrl.message.flags && i < 5; i++) {
var tag = $ctrl.message.flags[i];
var flagElement = angular.element('<div class="sg-category"></div>');
flagElement.css('left', (i*3) + 'px');
flagElement.css('background-color', $ctrl.service.$tags[tag][1]);
$element.prepend(flagElement);
}
// Mailbox name when in virtual mode
if ($ctrl.mailboxNameElement)
$ctrl.mailboxNameElement.innerHTML = $ctrl.message.$mailbox.$displayName;
// Sender or recipient when in
if ($ctrl.MailboxService.selectedFolder.type == 'sent')
$ctrl.senderElement.innerHTML = $ctrl.message.$shortAddress('to').encodeEntities();
else
$ctrl.senderElement.innerHTML = $ctrl.message.$shortAddress('from').encodeEntities();
// Priority icon
if ($ctrl.message.priority && $ctrl.message.priority.level < 3) {
$ctrl.priorityIconElement.classList.remove('ng-hide');
if ($ctrl.message.priority.level < 2)
$ctrl.priorityIconElement.classList.add('md-warn');
else
$ctrl.priorityIconElement.classList.remove('md-warn');
}
else
$ctrl.priorityIconElement.classList.add('ng-hide');
// Subject
$ctrl.subjectElement.innerHTML = $ctrl.message.subject.encodeEntities();
// Message size
$ctrl.sizeElement.innerHTML = $ctrl.message.size;
// Received Date
$ctrl.dateElement.innerHTML = $ctrl.message.relativedate;
setVisibility($ctrl.flagIconElement,
$ctrl.message.isflagged);
setVisibility($ctrl.answerIconElement,
$ctrl.message.isanswered);
setVisibility($ctrl.forwardIconElement,
$ctrl.message.isforwarded);
setVisibility($ctrl.attachmentIconElement,
$ctrl.message.hasattachment);
// Call original method on parent controller
angular.bind($ctrl.parentController, parentControllerOnUpdate)();
};
this.service = Message;
this.MailboxService = Mailbox;
};
}
angular
.module('SOGo.MailerUI')
.controller('sgMessageListItemMainController', sgMessageListItemMainController)
.directive('sgMessageListItemMain', sgMessageListItemMain);
})();