sogo/UI/WebServerResources/js/Mailer/MailboxController.js

604 lines
21 KiB
JavaScript

/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
MailboxController.$inject = ['$window', '$scope', '$timeout', '$q', '$state', '$mdDialog', '$mdToast', 'stateAccounts', 'stateAccount', 'stateMailbox', 'sgHotkeys', 'encodeUriFilter', 'sgSettings', 'sgFocus', 'Dialog', 'Preferences', 'Account', 'Mailbox'];
function MailboxController($window, $scope, $timeout, $q, $state, $mdDialog, $mdToast, stateAccounts, stateAccount, stateMailbox, sgHotkeys, encodeUriFilter, sgSettings, focus, Dialog, Preferences, Account, Mailbox) {
var vm = this,
defaultWindowTitle = angular.element($window.document).find('title').attr('sg-default') || "SOGo",
hotkeys = [],
sortLabels;
sortLabels = {
subject: 'Subject',
from: 'From',
date: 'Date',
size: 'Size',
arrival: 'Order Received'
};
this.$onInit = function() {
// Expose controller for eventual popup windows
$window.$mailboxController = vm;
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);
// 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 = '';
if (unseenCount)
title += '(' + unseenCount + ') ';
title += vm.selectedFolder.$displayName;
title += ' | ' + defaultWindowTitle;
$window.document.title = title;
});
};
function _registerHotkeys(keys) {
keys.push(sgHotkeys.createHotkey({
key: l('hotkey_search'),
description: l('Search'),
callback: vm.searchMode
}));
keys.push(sgHotkeys.createHotkey({
key: l('hotkey_compose'),
description: l('Write a new message'),
callback: function($event) {
if (vm.messageDialog === null)
vm.newMessage($event);
}
}));
keys.push(sgHotkeys.createHotkey({
key: l('hotkey_junk'),
description: l('Mark the selected messages as junk'),
callback: vm.markOrUnMarkMessagesAsJunk
}));
keys.push(sgHotkeys.createHotkey({
key: 'space',
description: l('Toggle item'),
callback: vm.toggleMessageSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'shift+space',
description: l('Toggle range of items'),
callback: vm.toggleMessageSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'up',
description: l('View next item'),
callback: _nextMessage,
preventInClass: ['sg-mail-part']
}));
keys.push(sgHotkeys.createHotkey({
key: 'down',
description: l('View previous item'),
callback: _previousMessage,
preventInClass: ['sg-mail-part']
}));
keys.push(sgHotkeys.createHotkey({
key: 'shift+up',
description: l('Add next item to selection'),
callback: _addNextMessageToSelection,
preventInClass: ['sg-mail-part']
}));
keys.push(sgHotkeys.createHotkey({
key: 'shift+down',
description: l('Add previous item to selection'),
callback: _addPreviousMessageToSelection,
preventInClass: ['sg-mail-part']
}));
_.forEach(['backspace', 'delete'], function(hotkey) {
keys.push(sgHotkeys.createHotkey({
key: hotkey,
description: l('Delete selected message or folder'),
callback: vm.confirmDeleteSelectedMessages
}));
});
// Register the hotkeys
_.forEach(keys, function(key) {
sgHotkeys.registerHotkey(key);
});
}
function _compactBeforeUnload(event) {
if (Mailbox.$virtualMode)
return true;
return vm.selectedFolder.$compact();
}
this.centerIsClose = function(navController_centerIsClose) {
// Allow the messages list to be hidden only if a message is selected
return this.selectedFolder.hasSelectedMessage() && !!navController_centerIsClose;
};
this.sort = function(field) {
if (field) {
vm.selectedFolder.$filter({ sort: field });
}
else {
return sortLabels[vm.service.$query.sort];
}
};
this.sortedBy = function(field) {
return Mailbox.$query.sort == field;
};
this.ascending = function() {
return Mailbox.$query.asc;
};
this.refresh = function () {
Preferences.pollInbox();
this.selectedFolder.$filter();
};
this.searchMode = function($event) {
vm.mode.search = true;
focus('search');
if ($event)
$event.preventDefault();
};
this.cancelSearch = function() {
vm.mode.search = false;
vm.selectedFolder.$filter(vm.service.$query).then(function() {
if (vm.selectedFolder.$selectedMessage) {
$timeout(function() {
vm.selectedFolder.$topIndex = vm.selectedFolder.uidsMap[vm.selectedFolder.$selectedMessage];
});
}
});
};
this.composeWindowEnabled = function() {
return Preferences.defaults.SOGoMailComposeWindowEnabled;
};
this.newMessage = function($event, inPopup) {
var message, onCompleteDeferred = $q.defer();
if (vm.messageDialog === null) {
if (inPopup || Preferences.defaults.SOGoMailComposeWindow == 'popup')
_newMessageInPopup();
else {
message = vm.account.$newMessage();
vm.messageDialog = $mdDialog
.show({
parent: angular.element(document.body),
targetEvent: $event,
clickOutsideToClose: false,
escapeToClose: false,
templateUrl: 'UIxMailEditor',
controller: 'MessageEditorController',
controllerAs: 'editor',
onComplete: function (scope, element) {
return onCompleteDeferred.resolve(element);
},
locals: {
stateParent: $scope,
stateAccount: vm.account,
stateMessage: message,
onCompletePromise: function () {
return onCompleteDeferred.promise;
}
}
})
.catch(_.noop) // Cancel
.finally(function() {
vm.messageDialog = null;
});
}
}
};
function _newMessageInPopup() {
var url = [sgSettings.baseURL(),
'UIxMailPopupView#!/Mail',
vm.account.id,
// The double-encoding is necessary
encodeUriFilter(encodeUriFilter(vm.selectedFolder.path)),
'new']
.join('/'),
wId = vm.selectedFolder.$id() + '/' + Math.random(0, 1000);
$window.open(url, wId,
["width=680",
"height=520",
"resizable=1",
"scrollbars=1",
"toolbar=0",
"location=0",
"directories=0",
"status=0",
"menubar=0",
"copyhistory=0"]
.join(','));
}
/**
* User has pressed up arrow key
*/
function _nextMessage($event) {
var index = vm.selectedFolder.$selectedMessageIndex();
if (angular.isDefined(index)) {
index--;
if (vm.selectedFolder.$topIndex > 0)
vm.selectedFolder.$topIndex--;
}
else {
// No message is selected, show oldest message
index = vm.selectedFolder.getLength() - 1;
vm.selectedFolder.$topIndex = vm.selectedFolder.getLength();
}
if (index > -1)
vm.selectMessage(vm.selectedFolder.getItemAtIndex(index));
$event.preventDefault();
return index;
}
/**
* User has pressed the down arrow key
*/
function _previousMessage($event) {
var index = vm.selectedFolder.$selectedMessageIndex();
if (angular.isDefined(index)) {
index++;
if (vm.selectedFolder.$topIndex < vm.selectedFolder.getLength())
vm.selectedFolder.$topIndex++;
}
else
// No message is selected, show newest
index = 0;
if (index < vm.selectedFolder.getLength())
vm.selectMessage(vm.selectedFolder.getItemAtIndex(index));
else
index = -1;
$event.preventDefault();
return index;
}
function _addNextMessageToSelection($event) {
var index;
if (vm.selectedFolder.hasSelectedMessage()) {
index = _nextMessage($event);
if (index >= 0)
vm.toggleMessageSelection($event, vm.selectedFolder.$messages[index]);
}
}
function _addPreviousMessageToSelection($event) {
var index;
if (vm.selectedFolder.hasSelectedMessage()) {
index = _previousMessage($event);
if (index >= 0)
vm.toggleMessageSelection($event, vm.selectedFolder.$messages[index]);
}
}
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});
};
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;
// Select closest range of messages when shift key is pressed
if ($event.shiftKey && folder.selectedCount() > 0) {
selectedIndex = folder.uidsMap[message.uid];
// Search for next selected message above
nextSelectedIndex = selectedIndex - 2;
while (nextSelectedIndex >= 0 &&
!folder.$messages[nextSelectedIndex].selected)
nextSelectedIndex--;
if (nextSelectedIndex < 0) {
// Search for next selected message bellow
nextSelectedIndex = selectedIndex + 2;
while (nextSelectedIndex < folder.getLength() &&
!folder.$messages[nextSelectedIndex].selected)
nextSelectedIndex++;
}
if (nextSelectedIndex >= 0 && nextSelectedIndex < folder.getLength()) {
for (i = Math.min(selectedIndex, nextSelectedIndex);
i <= Math.max(selectedIndex, nextSelectedIndex);
i++)
folder.$messages[i].selected = true;
}
}
folder.selectedMessages({ updateCache: true });
vm.mode.multiple = vm.selectedFolder.selectedCount();
$event.preventDefault();
$event.stopPropagation();
};
/**
* Batch operations
*/
function _currentMailboxes() {
if (Mailbox.$virtualMode)
return vm.selectedFolder.$mailboxes;
else
return [vm.selectedFolder];
}
// Unselect current message and cleverly load the next message.
// This function must not be called in virtual mode.
function _unselectMessage(message, index) {
var nextMessage, previousMessage, nextIndex = index;
vm.mode.multiple = vm.selectedFolder.selectedCount();
if (message) {
// Select either the next or previous message
if (index > 0) {
nextIndex -= 1;
nextMessage = vm.selectedFolder.$messages[nextIndex];
}
if (index < vm.selectedFolder.$messages.length)
previousMessage = vm.selectedFolder.$messages[index];
if (nextMessage) {
if (nextMessage.isread && previousMessage && !previousMessage.isread) {
nextIndex = index;
nextMessage = previousMessage;
}
}
else if (previousMessage) {
nextIndex = index;
nextMessage = previousMessage;
}
if (nextMessage) {
vm.selectedFolder.$topIndex = nextIndex;
$state.go('mail.account.mailbox.message', { messageId: nextMessage.uid });
}
else {
$state.go('mail.account.mailbox');
}
}
}
this.confirmDeleteSelectedMessages = function($event) {
var selectedMessages = vm.selectedFolder.selectedMessages();
if (vm.messageDialog === null && _.size(selectedMessages) > 0)
vm.messageDialog = Dialog.confirm(l('Confirmation'),
l('Are you sure you want to delete the selected messages?'),
{ ok: l('Delete') })
.then(function() {
var deleteSelectedMessage = vm.selectedFolder.hasSelectedMessage();
vm.selectedFolder.$deleteMessages(selectedMessages).then(function(index) {
if (Mailbox.$virtualMode) {
// When performing an advanced search, we refresh the view if the selected message
// was deleted, but only once all promises have completed.
if (deleteSelectedMessage)
$state.go('mail.account.virtualMailbox');
}
else {
// In normal mode, we immediately unselect the selected message.
_unselectMessage(deleteSelectedMessage, index);
}
}, function(response) {
vm.messageDialog = Dialog.confirm(l('Warning'),
l('The messages could not be moved to the trash folder. Would you like to delete them immediately?'),
{ ok: l('Delete') })
.then(function() {
vm.selectedFolder.$deleteMessages(selectedMessages, { withoutTrash: true })
.then(function(index) {
if (Mailbox.$virtualMode) {
// When performing an advanced search, we refresh the view if the selected message
// was deleted, but only once all promises have completed.
if (deleteSelectedMessage)
$state.go('mail.account.virtualMailbox');
}
else {
// In normal mode, we immediately unselect the selected message.
_unselectMessage(deleteSelectedMessage, index);
}
})
.finally(function() {
vm.messageDialog = null;
});
});
});
})
.finally(function() {
vm.messageDialog = null;
});
$event.preventDefault();
};
this.markOrUnMarkMessagesAsJunk = function() {
var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage();
var selectedMessages = vm.selectedFolder.selectedMessages();
if (_.size(selectedMessages) === 0 && moveSelectedMessage)
// No selection, user has pressed keyboard shortcut
selectedMessages = [vm.selectedFolder.selectedMessage()];
if (_.size(selectedMessages) > 0)
vm.selectedFolder.$markOrUnMarkMessagesAsJunk(selectedMessages).then(function() {
var dstFolder = '/' + vm.account.id + '/folderINBOX';
if (vm.selectedFolder.type != 'junk') {
dstFolder = '/' + vm.account.$getMailboxByType('junk').id;
}
vm.selectedFolder.$moveMessages(selectedMessages, dstFolder).then(function(index) {
if (Mailbox.$virtualMode) {
// When performing an advanced search, we refresh the view if the selected message
// was deleted, but only once all promises have completed.
if (moveSelectedMessage)
$state.go('mail.account.virtualMailbox');
}
else {
// In normal mode, we immediately unselect the selected message.
_unselectMessage(moveSelectedMessage, index);
}
});
});
};
this.copySelectedMessages = function(dstFolder) {
var selectedMessages = vm.selectedFolder.selectedMessages();
if (_.size(selectedMessages) > 0)
vm.selectedFolder.$copyMessages(selectedMessages, '/' + dstFolder).then(function() {
$mdToast.show(
$mdToast.simple()
.textContent(l('%{0} message(s) copied', vm.selectedFolder.selectedCount()))
.position('top right')
.hideDelay(2000));
});
};
this.moveSelectedMessages = function(dstFolder) {
var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage();
var selectedMessages = vm.selectedFolder.selectedMessages();
var count = vm.selectedFolder.selectedCount();
if (_.size(selectedMessages) > 0)
vm.selectedFolder.$moveMessages(selectedMessages, '/' + dstFolder).then(function(index) {
$mdToast.show(
$mdToast.simple()
.textContent(l('%{0} message(s) moved', count))
.position('top right')
.hideDelay(2000));
if (Mailbox.$virtualMode) {
// When performing an advanced search, we refresh the view if the selected message
// was moved, but only once all promises have completed.
if (moveSelectedMessage)
$state.go('mail.account.virtualMailbox');
}
else {
// In normal mode, we immediately unselect the selected message.
_unselectMessage(moveSelectedMessage, index);
}
});
};
this.selectAll = function() {
var count = 0;
_.forEach(_currentMailboxes(), function(folder) {
var i = 0, length = folder.$messages.length;
folder.$selectedMessages = [];
for (; i < length; i++) {
folder.$messages[i].selected = true;
folder.$selectedMessages.push(folder.$messages[i]);
}
count += length;
});
vm.mode.multiple = count;
};
this.unselectMessages = function() {
_.forEach(_currentMailboxes(), function(folder) {
folder.$selectedMessages = [];
_.forEach(folder.$messages, function(message) {
message.selected = false;
});
});
vm.mode.multiple = 0;
};
this.markSelectedMessagesAsFlagged = function() {
var selectedMessages = vm.selectedFolder.selectedMessages();
if (_.size(selectedMessages) > 0)
vm.selectedFolder.$flagMessages(selectedMessages, '\\Flagged', 'add').then(function(messages) {
_.forEach(messages, function(message) {
message.isflagged = true;
});
});
};
this.markSelectedMessagesAsUnread = function() {
var selectedMessages = vm.selectedFolder.selectedMessages();
if (_.size(selectedMessages) > 0) {
vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'remove').then(function(messages) {
_.forEach(messages, function(message) {
if (message.isread)
message.$mailbox.unseenCount++;
message.isread = false;
});
});
}
};
this.markSelectedMessagesAsRead = function() {
var selectedMessages = vm.selectedFolder.selectedMessages();
if (_.size(selectedMessages) > 0) {
vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'add').then(function(messages) {
_.forEach(messages, function(message) {
if (!message.isread)
message.$mailbox.unseenCount--;
message.isread = true;
});
});
}
};
}
angular
.module('SOGo.MailerUI')
.controller('MailboxController', MailboxController);
/**
* @ngInject
*/
mdVirtualRepeatContainerDirectiveDecorator.$inject = ['$delegate'];
function mdVirtualRepeatContainerDirectiveDecorator($delegate) {
$delegate[0].controller.prototype.resetScroll = function() {
// Don't scroll to top if current virtual repeater is the messages list
// but do update the container size
if (this.$element.parent().attr('id') == 'messagesList')
this.updateSize();
else
this.scrollTo(0);
};
return $delegate;
}
angular
.module('material.components.virtualRepeat')
.decorator('mdVirtualRepeatContainerDirective', mdVirtualRepeatContainerDirectiveDecorator);
})();