/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ (function() { 'use strict'; /** * @ngInject */ AddressBookController.$inject = ['$scope', '$q', '$window', '$state', '$timeout', '$mdDialog', '$mdToast', 'Account', 'Card', 'AddressBook', 'sgFocus', 'Dialog', 'sgSettings', 'sgHotkeys', 'stateAddressbooks', 'stateAddressbook']; function AddressBookController($scope, $q, $window, $state, $timeout, $mdDialog, $mdToast, Account, Card, AddressBook, focus, Dialog, Settings, sgHotkeys, stateAddressbooks, stateAddressbook) { var vm = this, hotkeys = [], sortLabels; sortLabels = { c_cn: 'Name', c_sn: 'Lastname', c_givenname: 'Firstname', c_mail: 'Email', c_screenname: 'Screen Name', c_o: 'Organization', c_telephonenumber: 'Preferred Phone' }; this.$onInit = function() { AddressBook.selectedFolder = stateAddressbook; this.service = AddressBook; this.selectedFolder = stateAddressbook; this.mode = { search: false, multiple: 0 }; _registerHotkeys(hotkeys); $scope.$on('$destroy', function() { // Deregister hotkeys _.forEach(hotkeys, function(key) { sgHotkeys.deregisterHotkey(key); }); }); }; function _registerHotkeys(keys) { keys.push(sgHotkeys.createHotkey({ key: l('hotkey_search'), description: l('Search'), callback: angular.bind(vm, vm.searchMode) })); keys.push(sgHotkeys.createHotkey({ key: l('key_create_card'), description: l('Create a new address book card'), callback: angular.bind(vm, vm.newComponent, 'card') })); keys.push(sgHotkeys.createHotkey({ key: l('key_create_list'), description: l('Create a new list'), callback: angular.bind(vm, vm.newComponent, 'list') })); keys.push(sgHotkeys.createHotkey({ key: 'space', description: l('Toggle item'), callback: angular.bind(vm, vm.toggleCardSelection) })); keys.push(sgHotkeys.createHotkey({ key: 'shift+space', description: l('Toggle range of items'), callback: angular.bind(vm, vm.toggleCardSelection) })); keys.push(sgHotkeys.createHotkey({ key: 'up', description: l('View next item'), callback: _nextCard })); keys.push(sgHotkeys.createHotkey({ key: 'down', description: l('View previous item'), callback: _previousCard })); keys.push(sgHotkeys.createHotkey({ key: 'shift+up', description: l('Add next item to selection'), callback: _addNextCardToSelection })); keys.push(sgHotkeys.createHotkey({ key: 'shift+down', description: l('Add previous item to selection'), callback: _addPreviousCardToSelection })); _.forEach(['backspace', 'delete'], function(hotkey) { keys.push(sgHotkeys.createHotkey({ key: hotkey, description: l('Delete selected card or address book'), callback: angular.bind(vm, vm.confirmDeleteSelectedCards) })); }); // Register the hotkeys _.forEach(keys, function(key) { sgHotkeys.registerHotkey(key); }); } this.centerIsClose = function(navController_centerIsClose) { // Allow the cards list to be hidden only if a card is selected return this.selectedFolder.hasSelectedCard() && !!navController_centerIsClose; }; this.selectCard = function(card) { $state.go('app.addressbook.card.view', {cardId: card.id}); }; this.toggleCardSelection = function($event, card) { var folder = this.selectedFolder, selectedIndex, nextSelectedIndex, i; if (!card) card = folder.$selectedCard(); card.selected = !card.selected; this.mode.multiple += card.selected? 1 : -1; // Select closest range of cards when shift key is pressed if ($event.shiftKey && folder.$selectedCount() > 1) { selectedIndex = folder.idsMap[card.id]; // Search for next selected card above nextSelectedIndex = selectedIndex - 2; while (nextSelectedIndex >= 0 && !folder.$cards[nextSelectedIndex].selected) nextSelectedIndex--; if (nextSelectedIndex < 0) { // Search for next selected card bellow nextSelectedIndex = selectedIndex + 2; while (nextSelectedIndex < folder.getLength() && !folder.$cards[nextSelectedIndex].selected) nextSelectedIndex++; } if (nextSelectedIndex >= 0 && nextSelectedIndex < folder.getLength()) { for (i = Math.min(selectedIndex, nextSelectedIndex); i <= Math.max(selectedIndex, nextSelectedIndex); i++) folder.$cards[i].selected = true; } } $event.preventDefault(); $event.stopPropagation(); }; this.newComponent = function(type) { $state.go('app.addressbook.new', { contactType: type }); }; this.unselectCards = function() { _.forEach(this.selectedFolder.$cards, function(card) { card.selected = false; }); this.mode.multiple = 0; }; /** * User has pressed up arrow key */ function _nextCard($event) { var index = vm.selectedFolder.$selectedCardIndex(); if (angular.isDefined(index)) { index--; if (vm.selectedFolder.$topIndex > 0) vm.selectedFolder.$topIndex--; } else { // No card is selected, show oldest card index = vm.selectedFolder.$cards.length() - 1; vm.selectedFolder.$topIndex = vm.selectedFolder.getLength(); } if (index > -1) vm.selectCard(vm.selectedFolder.$cards[index]); $event.preventDefault(); return index; } /** * User has pressed the down arrow key */ function _previousCard($event) { var index = vm.selectedFolder.$selectedCardIndex(); if (angular.isDefined(index)) { index++; if (vm.selectedFolder.$topIndex < vm.selectedFolder.$cards.length) vm.selectedFolder.$topIndex++; } else // No card is selected, show newest index = 0; if (index < vm.selectedFolder.$cards.length) vm.selectCard(vm.selectedFolder.$cards[index]); else index = -1; $event.preventDefault(); return index; } function _addNextCardToSelection($event) { var index; if (vm.selectedFolder.hasSelectedCard()) { index = _nextCard($event); if (index >= 0) toggleCardSelection($event, vm.selectedFolder.$cards[index]); } } function _addPreviousCardToSelection($event) { var index; if (vm.selectedFolder.hasSelectedCard()) { index = _previousCard($event); if (index >= 0) toggleCardSelection($event, vm.selectedFolder.$cards[index]); } } this.confirmDeleteSelectedCards = function($event) { var selectedCards = this.selectedFolder.$selectedCards(); if (this.selectedFolder.acls.objectEraser && _.size(selectedCards) > 0) Dialog.confirm(l('Warning'), l('Are you sure you want to delete the selected contacts?'), { ok: l('Delete') }) .then(function() { // User confirmed the deletion vm.selectedFolder.$deleteCards(selectedCards).then(function() { vm.mode.multiple = 0; if (!vm.selectedFolder.selectedCard) $state.go('app.addressbook'); }); }); $event.preventDefault(); }; /** * @see AddressBooksController.dragSelectedCards */ function _selectedCardsOperation(operation, dstId) { var srcFolder, allCards, cards, ids, clearCardView, promise, success; srcFolder = vm.selectedFolder; clearCardView = false; allCards = srcFolder.$selectedCards(); cards = _.filter(allCards, function(card) { return card.$isCard(); }); if (cards.length != allCards.length) $mdToast.show( $mdToast.simple() .textContent(l("Lists can't be moved or copied.")) .position('top right') .hideDelay(2000)); if (cards.length) { if (operation == 'copy') { promise = srcFolder.$copyCards(cards, dstId); success = l('%{0} card(s) copied', cards.length); } else { promise = srcFolder.$moveCards(cards, dstId); success = l('%{0} card(s) moved', cards.length); // Check if currently displayed card will be moved ids = _.map(cards, 'id'); clearCardView = (srcFolder.selectedCard && ids.indexOf(srcFolder.selectedCard) >= 0); } // Show success toast when action succeeds promise.then(function() { if (clearCardView) $state.go('app.addressbook'); $mdToast.show( $mdToast.simple() .textContent(success) .position('top right') .hideDelay(2000)); }); } } this.copySelectedCards = function(folder) { _selectedCardsOperation('copy', folder); }; this.moveSelectedCards = function(folder) { _selectedCardsOperation('move', folder); }; this.selectAll = function() { _.forEach(this.selectedFolder.$cards, function(card) { card.selected = true; }); this.mode.multiple = this.selectedFolder.$cards.length; }; this.sort = function(field) { if (field) { this.selectedFolder.$filter('', { sort: field }); } else { return sortLabels[AddressBook.$query.sort]; } }; this.sortedBy = function(field) { return AddressBook.$query.sort == field; }; this.ascending = function() { return AddressBook.$query.asc; }; this.searchMode = function($event) { vm.mode.search = true; focus('search'); if ($event) $event.preventDefault(); }; this.cancelSearch = function() { this.mode.search = false; this.selectedFolder.$filter(''); }; this.newMessage = function($event, recipients, recipientsField) { Account.$findAll().then(function(accounts) { var account = _.find(accounts, function(o) { if (o.id === 0) return o; }), onCompleteDeferred = $q.defer(); // We must initialize the Account with its mailbox // list before proceeding with message's creation account.$getMailboxes().then(function(mailboxes) { account.$newMessage().then(function(message) { message.editable[recipientsField] = recipients; $mdDialog.show({ parent: angular.element(document.body), targetEvent: $event, clickOutsideToClose: false, escapeToClose: false, templateUrl: '../Mail/UIxMailEditor', controller: 'MessageEditorController', controllerAs: 'editor', onComplete: function (scope, element) { return onCompleteDeferred.resolve(element); }, locals: { stateParent: $scope, stateAccount: account, stateMessage: message, onCompletePromise: function () { return onCompleteDeferred.promise; } } }); }); }); }); }; this.newMessageWithRecipient = function($event, recipient, fn) { var recipients = [fn + ' <' + recipient + '>']; this.newMessage($event, recipients, 'to'); $event.stopPropagation(); $event.preventDefault(); }; this.newMessageWithSelectedCards = function($event, recipientsField) { var selectedFolder = this.selectedFolder; var selectedCards = _.filter(this.selectedFolder.$cards, function(card) { return card.selected; }); var promises = [], recipients = []; _.forEach(selectedCards, function(card) { if (card.$isList({expandable: true})) { // If the list's members were already fetch, use them if (angular.isDefined(card.refs) && card.refs.length) { _.forEach(card.refs, function(ref) { if (ref.email.length) recipients.push(ref.$shortFormat()); }); } else { promises.push(card.$reload().then(function(card) { _.forEach(card.refs, function(ref) { if (ref.email.length) recipients.push(ref.$shortFormat()); }); })); } } else if (card.$loaded == Card.STATUS.LOADED) { if (card.c_mail) { recipients.push(card.$shortFormat()); } } else if (selectedFolder.$loadCard(card)) { promises.push(selectedFolder.$futureHeadersData.then(function() { var i = selectedFolder.idsMap[card.id]; if (angular.isDefined(i)) { var loadedCard = selectedFolder.$cards[i]; if (loadedCard.c_mail) recipients.push(loadedCard.$shortFormat()); } })); } }); $q.all(promises).then(function() { recipients = _.uniq(recipients); if (recipients.length) vm.newMessage($event, recipients, recipientsField); }); }; this.newListWithSelectedCards = function() { var selectedCards = _.filter(this.selectedFolder.$cards, function(card) { return card.selected; }); var promises = [], refs = []; _.forEach(selectedCards, function(card) { if (card.$isList({expandable: true})) { // If the list's members were already fetch, use them if (angular.isDefined(card.refs) && card.refs.length) { _.forEach(card.refs, function(ref) { if (ref.email.length) refs.push(ref); }); } else { promises.push(card.$reload().then(function(card) { _.forEach(card.refs, function(ref) { if (ref.email.length) refs.push(ref); }); })); } } else if (card.$$email && card.$$email.length) { refs.push(card); } }); $q.all(promises).then(function() { refs = _.uniqBy(_.map(refs, function(o) { return { reference: o.id || o.reference, email: o.$$email || o.email }; }), 'reference'); if (refs.length) $state.go('app.addressbook.new', { contactType: 'list', refs: refs }); }); }; } angular .module('SOGo.ContactsUI') .controller('AddressBookController', AddressBookController); })();