diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index 5f6224d91..27bd3bfe3 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import @@ -346,8 +347,9 @@ static Class SOGoContactGCSEntryK = Nil; CardElement *element; NSArray *elements, *values; NSMutableArray *units, *categories; + NSCalendarDate *date; id o; - unsigned int i; + unsigned int i, year, month, day; [card setNWithFamily: [attributes objectForKey: @"sn"] given: [attributes objectForKey: @"givenname"] @@ -355,7 +357,16 @@ static Class SOGoContactGCSEntryK = Nil; [card setNickname: [attributes objectForKey: @"nickname"]]; [card setFn: [attributes objectForKey: @"fn"]]; [card setTitle: [attributes objectForKey: @"title"]]; - [card setBday: [attributes objectForKey: @"birthday"]]; + + unsigned int seconds = [[NSString stringWithFormat: @"%@", [attributes objectForKey: @"birthday"]] intValue]; + if (seconds > 0) + { + date = [NSCalendarDate dateWithTimeIntervalSince1970: seconds]; + year = [date yearOfCommonEra]; + month = [date monthOfYear]; + day = [date dayOfMonth]; + [card setBday: [NSString stringWithFormat: @"%.4d%.2d%.2d", year, month, day]]; + } if ([[attributes objectForKey: @"addresses"] isKindOfClass: [NSArray class]]) { diff --git a/UI/Contacts/UIxContactsListActions.m b/UI/Contacts/UIxContactsListActions.m index 176b5610a..69d4bb57f 100644 --- a/UI/Contacts/UIxContactsListActions.m +++ b/UI/Contacts/UIxContactsListActions.m @@ -89,8 +89,13 @@ { id folder; NSString *ascending, *searchText, *valueText; + NSArray *results; + NSMutableArray *filteredContacts; + NSDictionary *contact; + BOOL excludeLists; NSComparisonResult ordering; WORequest *rq; + unsigned int i; if (!contactInfos) { @@ -107,12 +112,31 @@ else valueText = nil; + excludeLists = [[rq formValueForKey: @"excludeLists"] boolValue]; + [contactInfos release]; - contactInfos = [folder lookupContactsWithFilter: valueText - onCriteria: searchText - sortBy: [self sortKey] - ordering: ordering - inDomain: [[context activeUser] domain]]; + results = [folder lookupContactsWithFilter: valueText + onCriteria: searchText + sortBy: [self sortKey] + ordering: ordering + inDomain: [[context activeUser] domain]]; + if (excludeLists) + { + filteredContacts = [NSMutableArray array]; + for (i = 0; i < [results count]; i++) + { + contact = [results objectAtIndex: i]; + if (![[contact objectForKey: @"c_component"] isEqualToString: @"vlist"]) + { + [filteredContacts addObject: contact]; + } + } + contactInfos = [NSArray arrayWithArray: filteredContacts]; + } + else + { + contactInfos = results; + } [contactInfos retain]; } @@ -167,6 +191,7 @@ { id result; id folder; + BOOL excludeLists; NSString *searchText, *mail, *domain; NSDictionary *contact, *data; NSArray *contacts, *descriptors, *sortedContacts; @@ -176,6 +201,7 @@ WORequest *rq; rq = [context request]; + excludeLists = [[rq formValueForKey: @"excludeLists"] boolValue]; searchText = [rq formValueForKey: @"search"]; if ([searchText length] > 0) { @@ -198,9 +224,12 @@ for (i = 0; i < [contacts count]; i++) { contact = [contacts objectAtIndex: i]; - mail = [contact objectForKey: @"c_mail"]; - if ([mail isNotNull] && [uniqueContacts objectForKey: mail] == nil) - [uniqueContacts setObject: contact forKey: mail]; + if (!excludeLists || ![[contact objectForKey: @"c_component"] isEqualToString: @"vlist"]) + { + mail = [contact objectForKey: @"c_mail"]; + if ([mail isNotNull] && [uniqueContacts objectForKey: mail] == nil) + [uniqueContacts setObject: contact forKey: mail]; + } } if ([uniqueContacts count] > 0) diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox index 4a76231d5..5a608c115 100644 --- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox @@ -50,7 +50,7 @@ -
+

@@ -89,7 +89,7 @@
  • -
    +
    @@ -218,7 +218,7 @@
    - +
    @@ -227,13 +227,14 @@
    - {{addressbook.card.$birthday()}} + {{addressbook.card.$birthday() | date}}
    @@ -285,11 +286,14 @@
    -
    + -
    -
    + + + + diff --git a/UI/Templates/Themes/mobile/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/Themes/mobile/ContactsUI/UIxContactFoldersView.wox index fa19f3ded..ec5b35d54 100644 --- a/UI/Templates/Themes/mobile/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/Themes/mobile/ContactsUI/UIxContactFoldersView.wox @@ -16,7 +16,7 @@ var contactFolders = ; - + @@ -40,7 +40,7 @@ - + @@ -53,7 +53,9 @@ - + {{folder.name}} + + + @@ -77,7 +82,9 @@ - + {{card.c_cn || card.c_mail}} @@ -87,13 +94,30 @@ + + diff --git a/UI/WebServerResources/js/Common/ui-desktop.js b/UI/WebServerResources/js/Common/ui-desktop.js index 2c5cecf9c..f49581265 100644 --- a/UI/WebServerResources/js/Common/ui-desktop.js +++ b/UI/WebServerResources/js/Common/ui-desktop.js @@ -4,15 +4,24 @@ (function() { 'use strict'; - /* Dialog */ + /** + * @name Dialog (sgDialog factory in SOGo.UIDesktop) + * @desc Dialog object constructor + */ function Dialog() { } + /** + * @name alert + * @desc Show an alert dialog box with a single "OK" button + * @param {string} title + * @param {string} content + */ Dialog.alert = function(title, content) { var modal = this.$modal.open({ template: - '

    {{title}}

    ' + - '

    {{content}}

    ' + + '

    ' + + '

    ' + '' + l('OK') + '' + '', windowClass: 'small', @@ -26,11 +35,20 @@ }); }; - Dialog.confirm = function(title, content, callback) { + /** + * @name confirm + * @desc Show a confirmation dialog box with buttons "Cancel" and "OK" + * @param {string} title + * @param {string} content + * @returns a promise that always resolves, but returns true only if the user user has clicked on the + * 'OK' button + */ + Dialog.confirm = function(title, content) { + var d = this.$q.defer(); var modal = this.$modal.open({ template: - '

    {{title}}

    ' + - '

    {{content}}

    ' + + '

    ' + + '

    ' + '' + l('OK') + '' + '' + l('Cancel') + '' + '', @@ -40,18 +58,20 @@ $scope.content = content; $scope.closeModal = function() { $modalInstance.close(); + d.resolve(false); }; $scope.confirm = function() { - callback(); $modalInstance.close(); + d.resolve(true); }; } }); + return d.promise; }; /* The factory we'll use to register with Angular */ - Dialog.$factory = ['$modal', function($modal) { - angular.extend(Dialog, { $modal: $modal }); + Dialog.$factory = ['$modal', '$q', function($modal, $q) { + angular.extend(Dialog, { $modal: $modal, $q: $q }); return Dialog; // return constructor }]; diff --git a/UI/WebServerResources/js/Common/ui-mobile.js b/UI/WebServerResources/js/Common/ui-mobile.js index 96fd8c2cd..3e3e4000b 100644 --- a/UI/WebServerResources/js/Common/ui-mobile.js +++ b/UI/WebServerResources/js/Common/ui-mobile.js @@ -1,10 +1,10 @@ /* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* JavaScript for SOGoContacts */ +/* JavaScript for common UI services for mobile theme */ (function() { 'use strict'; - /* Constructor */ + /* Dialog */ function Dialog() { } @@ -13,29 +13,27 @@ title: title, template: content }); - // alertPopup.then(function(res) { - // console.log('Thank you for not eating my delicious ice cream cone'); - // }); + return alertPopup; }; + Dialog.confirm = function(title, content) { + var alertPopup = this.$ionicPopup.confirm({ + title: title, + template: content + }); + return alertPopup; + }; + + /* The factory we'll use to register with Angular */ Dialog.$factory = ['$ionicPopup', function($ionicPopup) { angular.extend(Dialog, { $ionicPopup: $ionicPopup }); return Dialog; // return constructor }]; + /* Angular module instanciation */ angular.module('SOGo.UIMobile', ['ionic']) + /* Factory registration in Angular module */ .factory('sgDialog', Dialog.$factory); - // angular.module('SOGo').factory('sgDialog', Dialog); - - // Dialog.prototype.alert = function(title, content) { - // var alertPopup = $ionicPopup.alert({ - // title: title, - // template: content - // }); - // alertPopup.then(function(res) { - // console.log('Thank you for not eating my delicious ice cream cone'); - // }); - // }; })(); diff --git a/UI/WebServerResources/js/Contacts/addressbook-model.js b/UI/WebServerResources/js/Contacts/addressbook-model.js index 6b3d83824..d639d5c38 100644 --- a/UI/WebServerResources/js/Contacts/addressbook-model.js +++ b/UI/WebServerResources/js/Contacts/addressbook-model.js @@ -25,7 +25,7 @@ }]; /* Factory registration in Angular module */ - angular.module('SOGo.Contacts').factory('sgAddressBook', AddressBook.$factory); + angular.module('SOGo.ContactsUI').factory('sgAddressBook', AddressBook.$factory); /* Set or get the list of addressbooks */ AddressBook.$all = function(data) { @@ -50,22 +50,35 @@ }); }; - AddressBook.prototype.$filter = function(search) { + /** + * @param {} [options] + */ + AddressBook.prototype.$filter = function(search, options) { var self = this; var params = { 'search': 'name_or_address', 'value': search, 'sort': 'c_cn', 'asc': 'true' }; + if (options && options.excludeLists) { + params.excludeLists = true; + } return this.$id().then(function(addressbook_id) { var futureAddressBookData = AddressBook.$$resource.filter(addressbook_id, params); return futureAddressBookData.then(function(data) { - self.cards = data.cards; + var cards; + if (options && options.dry) { + cards = data.cards; + } + else { + self.cards = data.cards; + cards = self.cards; + } // Instanciate Card objects - angular.forEach(self.cards, function(o, i) { - self.cards[i] = new AddressBook.$Card(o); + angular.forEach(cards, function(o, i) { + cards[i] = new AddressBook.$Card(o); }); - return self.cards; + return cards; }); }); }; @@ -84,10 +97,6 @@ var self = this; return this.$id().then(function(addressbook_id) { self.card = AddressBook.$Card.$find(addressbook_id, card_id); - self.card.$id().catch(function() { - // Card not found - self.card = null; - }); return self.card; }); }; diff --git a/UI/WebServerResources/js/Contacts/card-model.js b/UI/WebServerResources/js/Contacts/card-model.js index 12f4e5855..21f012ae4 100644 --- a/UI/WebServerResources/js/Contacts/card-model.js +++ b/UI/WebServerResources/js/Contacts/card-model.js @@ -13,11 +13,10 @@ var newCardData = Card.$$resource.newguid(this.pid); this.$unwrap(newCardData); } - return; } - - // The promise will be unwrapped first - this.$unwrap(futureCardData); + else + // The promise will be unwrapped first + this.$unwrap(futureCardData); } Card.$tel_types = ['work', 'home', 'cell', 'fax', 'pager']; @@ -36,7 +35,7 @@ }]; /* Factory registration in Angular module */ - angular.module('SOGo.Contacts') + angular.module('SOGo.ContactsUI') .factory('sgCard', Card.$factory) // Directive to format a postal address @@ -99,7 +98,7 @@ Card.prototype.$save = function() { var action = 'saveAsContact'; - if (this.tag == 'VLIST') action = 'saveAsList'; + if (this.tag == 'vlist') action = 'saveAsList'; //var action = 'saveAs' + this.tag.substring(1).capitalize(); return Card.$$resource.set([this.pid, this.id || '_new_'].join('/'), this.$omit(), @@ -162,20 +161,51 @@ return description.join(', '); }; - Card.prototype.$preferredEmail = function() { - var email = _.find(this.emails, function(o) { - return o.type == 'pref'; - }); + /** + * @name $preferredEmail + * @desc Returns the first email address of type "pref" or the first address if none found. + * @param {string} [partial] - a partial string that the email must match + */ + Card.prototype.$preferredEmail = function(partial) { + var email; + if (partial) { + var re = new RegExp(partial); + email = _.find(this.emails, function(o) { + return re.test(o.value); + }); + } if (email) { email = email.value; } - else if (this.emails && this.emails.length) { - email = this.emails[0].value; + else { + email = _.find(this.emails, function(o) { + return o.type == 'pref'; + }); + if (email) { + email = email.value; + } + else if (this.emails && this.emails.length) { + email = this.emails[0].value; + } + else { + email = ''; + } } return email; }; + /** + * + */ + Card.prototype.$shortFormat = function(partial) { + var fullname = this.$fullname(); + var email = this.$preferredEmail(partial); + if (email && email != fullname) + fullname += ' (' + email + ')'; + return fullname; + }; + Card.prototype.$birthday = function() { return new Date(this.birthday*1000); }; @@ -266,6 +296,35 @@ return this.addresses.length - 1; }; + Card.prototype.$addMember = function(email) { + if (angular.isUndefined(this.refs)) { + this.refs = [{email: email}]; + } + else { + for (var i = 0; i < this.refs.length; i++) { + if (this.refs[i].email == email) { + break; + } + } + if (i == this.refs.length) + this.refs.push({email: email}); + } + return this.refs.length - 1; + }; + + /** + * @name $updateMember + * @desc Update an existing list member from a Card instance. + * A list member has the following attribtues: + * - email + * - reference + * - fn + */ + Card.prototype.$updateMember = function(index, email, card) { + var ref = {'email': email, 'reference': card.c_name, 'fn': card.$fullname()}; + this.refs[index] = ref; + }; + // Unwrap a promise Card.prototype.$unwrap = function(futureCardData) { var self = this; diff --git a/UI/WebServerResources/js/ContactsUI.js b/UI/WebServerResources/js/ContactsUI.js index 8cfe578e2..fca766332 100644 --- a/UI/WebServerResources/js/ContactsUI.js +++ b/UI/WebServerResources/js/ContactsUI.js @@ -6,7 +6,7 @@ angular.module('SOGo.Common', []); - angular.module('SOGo.Contacts', ['ngSanitize', 'ui.router', 'mm.foundation', 'mm.foundation.offcanvas', 'SOGo.Common', 'SOGo.UIDesktop']) + angular.module('SOGo.ContactsUI', ['ngSanitize', 'ui.router', 'mm.foundation', 'SOGo.Common', 'SOGo.UIDesktop']) .constant('sgSettings', { 'baseURL': ApplicationBaseURL @@ -28,7 +28,7 @@ views: { 'card': { templateUrl: "card.html", - controller: 'cardCtrl' + controller: 'CardCtrl' } } }) @@ -36,8 +36,17 @@ url: "/:contact_type/new", views: { 'card': { - templateUrl: "card.html", - controller: 'cardCtrl' + templateUrl: "cardEditor.html", + controller: 'CardCtrl' + } + } + }) + .state('addressbook.editor', { + url: "/:card_id/edit", + views: { + 'card': { + templateUrl: "cardEditor.html", + controller: 'CardCtrl' } } }); @@ -100,10 +109,9 @@ $rootScope.addressbook.name = $rootScope.addressbooks[i].name; $rootScope.addressbook.$save() .then(function(data) { - console.debug("saved!"); $scope.editMode = false; }, function(data, status) { - console.debug("failed"); + Dialog.alert(l('Warning'), data); }); }; $scope.confirmDelete = function() { @@ -124,7 +132,6 @@ $scope.share = function() { var modal = $modal.open({ templateUrl: 'addressbookSharing.html', - //controller: 'addressbookSharingCtrl' controller: function($scope, $modalInstance) { $scope.closeModal = function() { $modalInstance.close(); @@ -159,10 +166,9 @@ }; }]) - .controller('cardCtrl', ['$scope', '$rootScope', 'sgAddressBook', 'sgCard', 'sgDialog', 'sgFocus', '$state', '$stateParams', function($scope, $rootScope, AddressBook, Card, Dialog, focus, $state, $stateParams) { + .controller('CardCtrl', ['$scope', '$rootScope', 'sgAddressBook', 'sgCard', 'sgDialog', 'sgFocus', '$state', '$stateParams', function($scope, $rootScope, AddressBook, Card, Dialog, focus, $state, $stateParams) { if ($stateParams.card_id) { - // Show existing card - if ($rootScope.addressbook == null) { + if (!$rootScope.addressbook) { // Card is directly access with URL fragment $rootScope.addressbook = AddressBook.$find($stateParams.addressbook_id); } @@ -175,16 +181,14 @@ $scope.addressbook.card = new Card({ 'pid': $stateParams.addressbook_id, 'tag': tag }); $scope.editMode = true; } + $scope.allEmailTypes = Card.$email_types; $scope.allTelTypes = Card.$tel_types; $scope.allUrlTypes = Card.$url_types; $scope.allAddressTypes = Card.$address_types; - $scope.edit = function() { - $rootScope.master_card = angular.copy($rootScope.addressbook.card); - $scope.editMode = true; - console.debug('edit'); - }; + $rootScope.master_card = angular.copy($rootScope.addressbook.card); + $scope.addOrgUnit = function() { var i = $rootScope.addressbook.card.$addOrgUnit(''); focus('orgUnit_' + i); @@ -209,12 +213,14 @@ var i = $rootScope.addressbook.card.$addAddress('', '', '', '', '', '', '', ''); focus('address_' + i); }; - $scope.save = function(cardForm) { - if (cardForm.$valid) { + $scope.addMember = function() { + var i = $rootScope.addressbook.card.$addMember(''); + focus('ref_' + i); + }; + $scope.save = function(form) { + if (form.$valid) { $rootScope.addressbook.card.$save() .then(function(data) { - console.debug("saved!"); - $scope.editMode = false; var i = _.indexOf(_.pluck($rootScope.addressbook.cards, 'id'), $rootScope.addressbook.card.id); if (i < 0) { // Reload contacts list and show addressbook in which the card has been created @@ -224,6 +230,7 @@ // Update contacts list with new version of the Card object $rootScope.addressbook.cards[i] = angular.copy($rootScope.addressbook.card); } + $state.go('addressbook.card'); }, function(data, status) { console.debug("failed"); }); @@ -231,26 +238,30 @@ }; $scope.cancel = function() { $scope.reset(); - $scope.editMode = false; + //$scope.editMode = false; + delete $rootScope.master_card; + $state.go('addressbook.card', { card_id: $scope.addressbook.card.id }); }; $scope.reset = function() { $rootScope.addressbook.card = angular.copy($rootScope.master_card); }; $scope.confirmDelete = function(card) { Dialog.confirm(l('Warning'), - l('Are you sure you want to delete the card of "%{0}"?', card.$fullname()), - function() { - card.$delete() - .then(function() { - $rootScope.addressbook.cards = _.reject($rootScope.addressbook.cards, function(o) { - return o.id == card.id; - }); - $rootScope.addressbook.card = null; - }, function(data, status) { - Dialog.alert(l('Warning'), l('An error occured while deleting the card "%{0}".', - card.$fullname())); - }); - }); + l('Are you sure you want to delete the card of %{0}?', card.$fullname())) + .then(function(res) { + if (res) { + card.$delete() + .then(function() { + $rootScope.addressbook.cards = _.reject($rootScope.addressbook.cards, function(o) { + return o.id == card.id; + }); + delete $rootScope.addressbook.card; + }, function(data, status) { + Dialog.alert(l('Warning'), l('An error occured while deleting the card "%{0}".', + card.$fullname())); + }); + } + }); }; }]); diff --git a/UI/WebServerResources/js/mobile/ContactsUI.js b/UI/WebServerResources/js/mobile/ContactsUI.js index 972f8a030..cc72a58f3 100644 --- a/UI/WebServerResources/js/mobile/ContactsUI.js +++ b/UI/WebServerResources/js/mobile/ContactsUI.js @@ -6,7 +6,7 @@ angular.module('SOGo.Common', []); - angular.module('SOGo.Contacts', ['ionic', 'SOGo.Common', 'SOGo.Contacts']) + angular.module('SOGo.ContactsUI', ['ionic', 'SOGo.Common', 'SOGo.UIMobile']) .constant('sgSettings', { 'baseURL': ApplicationBaseURL @@ -16,10 +16,10 @@ $ionicPlatform.ready(function() { // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard // for form inputs) - if(window.cordova && window.cordova.plugins.Keyboard) { + if (window.cordova && window.cordova.plugins.Keyboard) { cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); } - if(window.StatusBar) { + if (window.StatusBar) { // org.apache.cordova.statusbar required StatusBar.styleDefault(); } @@ -50,17 +50,51 @@ views: { 'menuContent': { templateUrl: "addressbook.html", - controller: 'AddressBookCtrl' + controller: 'AddressBookCtrl', + resolve: { + stateAddressbook: function($stateParams, sgAddressBook) { + return sgAddressBook.$find($stateParams.addressbook_id); + } + } } } }) - .state('app.contact', { + .state('app.newCard', { + url: "/addressbook/:addressbook_id/:contact_type/new", + views: { + 'menuContent': { + templateUrl: "card.html", + controller: 'CardCtrl', + resolve: { + stateCard: function($rootScope, $stateParams, sgAddressBook, sgCard) { + var tag = 'v' + $stateParams.contact_type; + if (!$rootScope.addressbook) { + $rootScope.addressbook = sgAddressBook.$find($stateParams.addressbook_id); + } + return new sgCard({ 'pid': $stateParams.addressbook_id, + 'tag': tag, + 'isNew': true }); + } + } + } + } + }) + + .state('app.card', { url: "/addressbook/:addressbook_id/:card_id", views: { 'menuContent': { templateUrl: "card.html", - controller: 'CardCtrl' + controller: 'CardCtrl', + resolve: { + stateCard: function($rootScope, $stateParams, sgAddressBook) { + if (!$rootScope.addressbook) { + $rootScope.addressbook = sgAddressBook.$find($stateParams.addressbook_id); + } + return $rootScope.addressbook.$getCard($stateParams.card_id); + } + } } } }); @@ -69,29 +103,6 @@ $urlRouterProvider.otherwise('/app/addressbooks'); }) -// .directive('sgAddress', function() { -// return { -// restrict: 'A', -// replace: false, -// scope: { data: '=sgAddress' }, -// controller: ['$scope', function($scope) { -// $scope.addressLines = function(data) { -// var lines = []; -// if (data.street) lines.push(data.street); -// if (data.street2) lines.push(data.street2); -// var locality_region = []; -// if (data.locality) locality_region.push(data.locality); -// if (data.region) locality_region.push(data.region); -// if (locality_region.length > 0) lines.push(locality_region.join(', ')); -// if (data.country) lines.push(data.country); -// if (data.postalcode) lines.push(data.postalcode); -// return lines.join('
    '); -// }; -// }], -// template: '
    ' -// } -// }) - .controller('AppCtrl', ['$scope', '$http', function($scope, $http) { $scope.UserLogin = UserLogin; $scope.UserFolderURL = UserFolderURL; @@ -107,29 +118,16 @@ .controller('AddressBooksCtrl', ['$scope', '$rootScope', '$timeout', 'sgAddressBook', function($scope, $rootScope, $timeout, AddressBook) { // Initialize with data from template $scope.addressbooks = AddressBook.$all(contactFolders); - // $scope.select = function(rowIndex) { - // $rootScope.selectedAddressBook = $rootScope.addressbooks[rowIndex]; - // }; - // $scope.rename = function() { - // console.debug("rename folder"); - // $scope.editMode = $rootScope.addressbook.id; - // //focus('folderName'); - // }; - // $scope.save = function() { - // console.debug("save addressbook"); - // $rootScope.addressbook.$save() - // .then(function(data) { - // console.debug("saved!"); - // $scope.editMode = false; - // }, function(data, status) { - // console.debug("failed"); - // }); - // }; + $scope.edit = function(i) { + + }; + $scope.save = function(i) { + + }; }]) - .controller('AddressBookCtrl', ['$scope', '$rootScope', '$stateParams', 'sgAddressBook', function($scope, $rootScope, $stateParams, AddressBook) { - var id = $stateParams.addressbook_id; - $rootScope.addressbook = AddressBook.$find(id); + .controller('AddressBookCtrl', ['$scope', '$rootScope', '$stateParams', '$state', 'sgAddressBook', 'sgCard', 'stateAddressbook', function($scope, $rootScope, $stateParams, $state, AddressBook, Card, stateAddressbook) { + $rootScope.addressbook = stateAddressbook; $scope.search = { 'status': null, 'filter': null, 'last_filter': null }; $scope.doSearch = function(keyEvent) { @@ -144,7 +142,7 @@ } else if ($scope.search.filter.length == 0) { $scope.searchStatus = ''; - $rootScope.addressbook = AddressBook.$find(id); + $rootScope.addressbook = AddressBook.$find($rootScope.addressbook.id); } else { $scope.search.status = 'min-char'; @@ -153,15 +151,126 @@ } $scope.search.last_filter = $scope.search.filter; }; - }]) - .controller('CardCtrl', ['$scope', '$rootScope', '$stateParams', 'sgAddressBook', 'sgCard', function($scope, $rootScope, $stateParams, AddressBook, Card) { + .controller('CardCtrl', ['$scope', '$rootScope', '$state', '$stateParams', '$ionicModal', 'sgDialog', 'sgAddressBook', 'sgCard', 'stateCard', function($scope, $rootScope, $state, $stateParams, $ionicModal, Dialog, AddressBook, Card, stateCard) { + $rootScope.addressbook.card = stateCard; + $scope.UserFolderURL = UserFolderURL; - if (!$rootScope.addressbook) { - $rootScope.addressbook = AddressBook.$find($stateParams.addressbook_id); + $scope.allEmailTypes = Card.$email_types; + $scope.allTelTypes = Card.$tel_types; + $scope.allUrlTypes = Card.$url_types; + $scope.allAddressTypes = Card.$address_types; + + $scope.edit = function() { + // Copy card to be able to cancel changes later + $scope.master_card = angular.copy($rootScope.addressbook.card); + // Build modal editor + $ionicModal.fromTemplateUrl('cardEditor.html', { + scope: $scope, + focusFirstInput: false + }).then(function(modal) { + if ($scope.$cardEditorModal) { + // Delete previous modal + $scope.$cardEditorModal.remove(); + } + $scope.$cardEditorModal = modal; + // Show modal + $scope.$cardEditorModal.show(); + }); + }; + $scope.cancel = function() { + if ($rootScope.addressbook.card.isNew) { + $scope.$cardEditorModal.hide().then(function() { + // Go back to addressbook + $state.go('app.addressbook', { addressbook_id: $rootScope.addressbook.id }); + }); + } + else { + $rootScope.addressbook.card = angular.copy($scope.master_card); + $scope.$cardEditorModal.hide() + } + }; + $scope.addOrgUnit = function() { + var i = $rootScope.addressbook.card.$addOrgUnit(''); + focus('orgUnit_' + i); + }; + $scope.addCategory = function() { + var i = $rootScope.addressbook.card.$addCategory($scope.new_category); + focus('category_' + i); + }; + $scope.addEmail = function() { + var i = $rootScope.addressbook.card.$addEmail($scope.new_email_type); + focus('email_' + i); + }; + $scope.addPhone = function() { + var i = $rootScope.addressbook.card.$addPhone($scope.new_phone_type); + focus('phone_' + i); + }; + $scope.addUrl = function() { + var i = $rootScope.addressbook.card.$addUrl('', ''); + focus('url_' + i); + }; + $scope.addAddress = function() { + var i = $rootScope.addressbook.card.$addAddress('', '', '', '', '', '', '', ''); + focus('address_' + i); + }; + $scope.addMember = function() { + var i = $rootScope.addressbook.card.$addMember(''); + focus('ref_' + i); + }; + $scope.save = function(form) { + if (form.$valid) { + $rootScope.addressbook.card.$save() + .then(function(data) { + delete $rootScope.addressbook.card.isNew; + var i = _.indexOf(_.pluck($rootScope.addressbook.cards, 'id'), $rootScope.addressbook.card.id); + if (i < 0) { + // New card + // Reload contacts list and show addressbook in which the card has been created + var card = angular.copy($rootScope.addressbook.card); + $rootScope.addressbook = AddressBook.$find(data.pid); + $rootScope.addressbook.card = card; + } + else { + // Update contacts list with new version of the Card object + $rootScope.addressbook.cards[i] = angular.copy($rootScope.addressbook.card); + } + // Close editor + $scope.$cardEditorModal.hide(); + }); + } + }; + $scope.confirmDelete = function(card) { + Dialog.confirm(l('Warning'), + l('Are you sure you want to delete the card of %{0}?', card.$fullname())) + .then(function(res) { + if (res) { + // User has confirmed deletion + card.$delete() + .then(function() { + // Delete card from list of addressbook + $rootScope.addressbook.cards = _.reject($rootScope.addressbook.cards, function(o) { + return o.id == card.id; + }); + // Delete card object + delete $rootScope.addressbook.card; + // Delete modal editor + $scope.$cardEditorModal.remove(); + // Go back to addressbook + $state.go('app.addressbook', { addressbook_id: $rootScope.addressbook.id }); + }, function(data, status) { + Dialog.alert(l('Warning'), l('An error occured while deleting the card "%{0}".', + card.$fullname())); + }); + } + }); + }; + + if ($scope.addressbook.card && $scope.addressbook.card.isNew) { + // New contact + $scope.edit(); } - $rootScope.addressbook.$getCard($stateParams.card_id); - }]) + }]); })(); diff --git a/UI/WebServerResources/scss/mobile.scss b/UI/WebServerResources/scss/mobile.scss index 8aebd06b7..f3e9d0b40 100644 --- a/UI/WebServerResources/scss/mobile.scss +++ b/UI/WebServerResources/scss/mobile.scss @@ -1,9 +1,26 @@ @import "ionic"; +.ion-plus-circled { + color: $balanced; +} + +.button { + &.button-icon { + &.button-assertive { + color: $assertive; + } + &.button-small { + &.icon:before { + font-size: $button-small-icon-size; + } + } + } +} + .list-clear { - .list { + > .list { border-top: 1px solid $item-light-border; - ion-item { + > ion-item { border: 0; &, a { padding-top: 5px !important; @@ -23,13 +40,37 @@ ion-content { margin-right: 5px; } } + .list-address { + &, .list { + border-left: $item-border-width solid $item-default-border; + } + ion-item { + border: 0; + padding-left: 2px !important; + padding-right: 0px; + &.item-input { + input { + flex: 1 0 120px; + } + } + } + } } ion-item { small { display: block; - color: $positive; - } - .list-clear & { + color: $positive !important; } +} + +/* Additional styles */ + +.label { + @extend .button-small; + @include button-style($button-dark-bg, $button-dark-border, $button-dark-active-bg, $button-dark-active-border, $button-dark-text); + @include button-clear($button-dark-bg); + @include button-outline($button-dark-bg); + border-radius: 4px; + margin-left: 4px; } \ No newline at end of file