diff --git a/UI/WebServerResources/js/Contacts.services.js b/UI/WebServerResources/js/Contacts.services.js index a052a0fcc..fdecb858f 100644 --- a/UI/WebServerResources/js/Contacts.services.js +++ b/UI/WebServerResources/js/Contacts.services.js @@ -1,2 +1,2 @@ -!function(){"use strict";function a(b){if("function"!=typeof b.then)if(this.init(b),this.name&&!this.id){var c=a.$$resource.create("createFolder",this.name);this.$unwrap(c),this.acls={objectEditor:1,objectCreator:1,objectEraser:1}}else this.id&&(this.$acl=new a.$$Acl("Contacts/"+this.id));else this.$unwrap(b)}a.$factory=["$q","$timeout","$log","sgSettings","sgAddressBook_PRELOAD","Resource","Card","Acl","Preferences",function(b,c,d,e,f,g,h,i,j){return angular.extend(a,{$q:b,$timeout:c,$log:d,PRELOAD:f,$$resource:new g(e.activeUser("folderURL")+"Contacts",e.activeUser()),$Card:h,$$Acl:i,$Preferences:j,$query:{search:"name_or_address",value:"",sort:"c_cn",asc:1},activeUser:e.activeUser(),selectedFolder:null,$refreshTimeout:null}),j.ready().then(function(){j.settings.Contact.SortingState&&(a.$query.sort=j.settings.Contact.SortingState[0],a.$query.asc=parseInt(j.settings.Contact.SortingState[1]))}),a}];try{angular.module("SOGo.ContactsUI")}catch(a){angular.module("SOGo.ContactsUI",["SOGo.Common","SOGo.PreferencesUI"])}angular.module("SOGo.ContactsUI").constant("sgAddressBook_PRELOAD",{LOOKAHEAD:50,SIZE:100}).factory("AddressBook",a.$factory),a.$filterAll=function(b,c,d){var e={search:b};return b?(angular.isUndefined(a.$cards)&&(a.$cards=[]),angular.extend(e,c),a.$$resource.fetch(null,"allContactSearch",e).then(function(c){var e,f,g,h=function(a){return this.id==a.id};for(e=d?_.filter(c.contacts,function(a){return _.isUndefined(_.find(d,_.bind(h,a)))}):c.contacts,g=a.$cards.length-1;g>=0;g--)f=a.$cards[g],_.isUndefined(_.find(e,_.bind(h,f)))&&a.$cards.splice(g,1);return _.forEach(e,function(c,d){if(_.isUndefined(_.find(a.$cards,_.bind(h,c)))){var e=new a.$Card(_.mapKeys(c,function(a,b){return b.toLowerCase()}),b);a.$cards.splice(d,0,e)}}),a.$log.debug(a.$cards),a.$cards})):(a.$cards=[],a.$q.when(a.$cards))},a.$add=function(a){var b,c,d;b=a.isSubscription?this.$subscriptions:this.$addressbooks,c=_.find(b,function(b){return"personal"==a.id||"personal"!=b.id&&1===b.name.localeCompare(a.name)}),d=c?_.indexOf(_.map(b,"id"),c.id):1,b.splice(d,0,a)},a.$findAll=function(b){var c=this;return b&&(this.$addressbooks=[],this.$subscriptions=[],this.$remotes=[],angular.forEach(b,function(b,d){var e=new a(b);e.isRemote?c.$remotes.push(e):e.isSubscription?c.$subscriptions.push(e):c.$addressbooks.push(e)})),_.union(this.$addressbooks,this.$subscriptions,this.$remotes)},a.$subscribe=function(b,c){var d=this;return a.$$resource.userResource(b).fetch(c,"subscribe").then(function(b){var c=new a(b);return _.isUndefined(_.find(d.$subscriptions,function(a){return a.id==b.id}))&&a.$add(c),c})},a.$reloadAll=function(){var b=this;return a.$$resource.fetch("addressbooksList").then(function(c){_.forEach(c.addressbooks,function(c){var d,e;d=c.isRemote?b.$remotes:c.owner!=a.activeUser.login?b.$subscriptions:b.$addressbooks,e=_.find(d,function(a){return a.id==c.id}),e&&e.init(c)})})},a.prototype.init=function(b,c){var d=this;this.$$cards||(this.$$cards=[]),this.idsMap={},this.$cards=[],angular.forEach(b,function(a,b){"headers"!=b&&"cards"!=b&&(d[b]=a)}),this.isOwned=a.activeUser.isSuperUser||this.owner==a.activeUser.login,this.isSubscription=!this.isRemote&&this.owner!=a.activeUser.login},a.prototype.$id=function(){return this.id?a.$q.when(this.id):this.$futureAddressBookData.then(function(a){return a.id})},a.prototype.getLength=function(){return this.$cards.length},a.prototype.getItemAtIndex=function(a){var b;return!this.$isLoading&&a>=0&&a0&&(f=a.$$resource.post(this.id,"headers",{ids:e}),this.$unwrapHeaders(f))}return j},a.prototype.hasSelectedCard=function(){return angular.isDefined(this.selectedCard)},a.prototype.isSelectedCard=function(a){return this.hasSelectedCard()&&this.selectedCard==a},a.prototype.$selectedCard=function(){var a=this;return _.find(this.$cards,function(b){return b.id==a.selectedCard})},a.prototype.$selectedCardIndex=function(){return _.indexOf(_.map(this.$cards,"id"),this.selectedCard)},a.prototype.$selectedCards=function(){return _.filter(this.$cards,function(a){return a.selected})},a.prototype.$selectedCount=function(){var a;return a=0,this.$cards&&(a=_.filter(this.$cards,function(a){return a.selected}).length),a},a.prototype.$startRefreshTimeout=function(){var b=this;a.$refreshTimeout&&a.$timeout.cancel(a.$refreshTimeout),a.$Preferences.ready().then(function(){var c=a.$Preferences.defaults.SOGoRefreshViewCheck;if(c&&"manually"!=c){var d=angular.bind(b,a.prototype.$reload);a.$refreshTimeout=a.$timeout(d,1e3*c.timeInterval())}})},a.prototype.$reload=function(){return this.$startRefreshTimeout(),this.$filter()},a.prototype.$filter=function(b,c,d){var e,f=this,g=c&&c.dry;return g?e=angular.copy(a.$query):(this.$isLoading=!0,e=a.$query,this.isRemote||(e.partial=1)),a.$Preferences.ready().then(function(){return c&&(angular.extend(e,c),g&&!b)?(f.$$cards=[],a.$q.when(f.$$cards)):(angular.isDefined(b)&&(e.value=b),f.$id().then(function(c){var h=a.$$resource.fetch(c,"view",e);return g?h.then(function(e){var g,h,i,j,k,l=f.$$cards,m=function(a){return this==a.id};for(e.headers&&(j=_.invokeMap(e.headers[0],"toLowerCase"),k=j.indexOf("id"),e.headers.splice(0,1)),g=d?_.filter(e.ids,function(a){return _.isUndefined(_.find(d,_.bind(m,a)))}):e.ids,i=l.length-1;i>=0;i--)h=l[i],_.isUndefined(_.find(g,_.bind(m,h.id)))&&l.splice(i,1);return _.forEach(g,function(d,e){if(_.isUndefined(_.find(l,_.bind(m,d)))){var f={pid:c,id:d},g=new a.$Card(f,b);l.splice(e,0,g)}}),_.forEach(g,function(a,b){var c,d;l[b].id!=a&&(c=_.findIndex(l,_.bind(m,a)),d=l.splice(c,1),l.splice(b,0,d[0]))}),_.forEach(e.headers,function(a){var c,d=_.findIndex(l,_.bind(m,a[k]));d>-1&&(c=_.zipObject(j,a),l[d].init(c,b))}),l}):f.$unwrap(h)}))})},a.prototype.$rename=function(b){var c=_.indexOf(_.map(a.$addressbooks,"id"),this.id);return this.name=b,a.$addressbooks.splice(c,1),a.$add(this),this.$save()},a.prototype.$delete=function(){var b,c,d=this,e=a.$q.defer();return this.isSubscription?(c=a.$$resource.fetch(this.id,"unsubscribe"),b=a.$subscriptions):(c=a.$$resource.remove(this.id),b=a.$addressbooks),c.then(function(){var a=_.indexOf(_.map(b,"id"),d.id);b.splice(a,1),e.resolve()},e.reject),e.promise},a.prototype.$_deleteCards=function(a){var b=this;_.forEachRight(this.$cards,function(c,d){var e=_.findIndex(a,function(a){return c.id==a});e>-1?(a.splice(e,1),delete b.idsMap[c.id],b.isSelectedCard(c.id)&&delete b.selectedCard,b.$cards.splice(d,1)):b.idsMap[c.id]-=a.length})},a.prototype.$deleteCards=function(b){var c=this,d=_.map(b,"id");return a.$$resource.post(this.id,"batchDelete",{uids:d}).then(function(){c.$_deleteCards(d)})},a.prototype.$copyCards=function(b,c){var d=_.map(b,"id");return a.$$resource.post(this.id,"copy",{uids:d,folder:c})},a.prototype.$moveCards=function(b,c){var d,e=this;return d=_.map(b,"id"),a.$$resource.post(this.id,"move",{uids:d,folder:c}).then(function(){return e.$_deleteCards(d)})},a.prototype.$save=function(){return a.$$resource.save(this.id,this.$omit()).then(function(a){return a})},a.prototype.exportCards=function(b){var c;if(b){var d=_.filter(this.$cards,function(a){return a.selected});c=_.map(d,"id")}return a.$$resource.download(this.id,"export",angular.isDefined(c)?{uids:c}:null,{type:"application/octet-stream"})},a.prototype.$unwrap=function(b){var c=this;this.$isLoading=!0,this.$futureAddressBookData=b.then(function(b){return a.$timeout(function(){var d;return(!b.ids||c.$topIndex>b.ids.length-1)&&(c.$topIndex=0),angular.forEach(a.$findAll(),function(a,d){a.id==b.id&&angular.extend(c,a)}),c.init(b),c.ids&&(a.$log.debug("unwrapping "+c.ids.length+" cards"),_.reduce(c.ids,function(b,d,e){var f={pid:c.id,id:d};return c.idsMap[f.id]=e,b.push(new a.$Card(f)),b},c.$cards)),b.headers&&(d=_.invokeMap(b.headers[0],"toLowerCase"),b.headers.splice(0,1),c.ids?_.forEach(b.headers,function(a){var b=_.zipObject(d,a),e=c.idsMap[b.id];c.$cards[e].init(b)}):(c.$cards=[],angular.forEach(b.headers,function(b){var e=_.zipObject(d,b);angular.extend(e,{pid:c.id}),c.$cards.push(new a.$Card(e))}))),c.$acl=new a.$$Acl("Contacts/"+c.id),c.$startRefreshTimeout(),c.$isLoading=!1,a.$log.debug("addressbook "+c.id+" ready"),c})},function(b){c.isError=!0,angular.isObject(b)&&a.$timeout(function(){angular.extend(c,b)})})},a.prototype.$unwrapHeaders=function(b){var c=this;b.then(function(b){a.$timeout(function(){var a,d;b.length>0&&(a=_.invokeMap(b[0],"toLowerCase"),b.splice(0,1),_.forEach(b,function(b){b=_.zipObject(a,b),d=c.idsMap[b.id],angular.isDefined(d)&&c.$cards[d].init(b)}))})})},a.prototype.$omit=function(){var a={};return angular.forEach(this,function(b,c){"constructor"!=c&&"ids"!=c&&"$"!=c[0]&&(a[c]=b)}),a}}(),function(){"use strict";function a(b,c){if("function"!=typeof b.then){if(this.init(b,c),this.pid&&!this.id){var d=a.$$resource.newguid(this.pid);this.$unwrap(d),this.isNew=!0}}else this.$unwrap(b)}a.$TEL_TYPES=["work","home","cell","fax","pager"],a.$EMAIL_TYPES=["work","home","pref"],a.$URL_TYPES=["work","home","pref"],a.$ADDRESS_TYPES=["work","home"],a.$factory=["$timeout","sgSettings","sgCard_STATUS","Resource","Preferences",function(b,c,d,e,f){return angular.extend(a,{STATUS:d,$$resource:new e(c.activeUser("folderURL")+"Contacts",c.activeUser()),$timeout:b,$Preferences:f}),f.ready().then(function(){f.defaults.SOGoContactsCategories&&(a.$categories=f.defaults.SOGoContactsCategories),f.defaults.SOGoAlternateAvatar&&(a.$alternateAvatar=f.defaults.SOGoAlternateAvatar)}),a}];try{angular.module("SOGo.ContactsUI")}catch(a){angular.module("SOGo.ContactsUI",["SOGo.Common","SOGo.PreferencesUI"])}angular.module("SOGo.ContactsUI").constant("sgCard_STATUS",{NOT_LOADED:0,DELAYED_LOADING:1,LOADING:2,LOADED:3,DELAYED_MS:300}).factory("Card",a.$factory),a.$find=function(b,c){var d=this.$$resource.fetch([b,c].join("/"),"view");return c?new a(d):a.$unwrapCollection(d)},a.filterCategories=function(b){var c=new RegExp(b,"i");return _.map(_.filter(a.$categories,function(a){return a.search(c)!=-1}),function(a){return{value:a}})},a.$unwrapCollection=function(b){var c={};return c.$futureCardData=b,b.then(function(b){a.$timeout(function(){angular.forEach(b,function(b,d){c[b.id]=new a(b)})})}),c},a.prototype.init=function(b,c){var d=this;this.refs=[],this.categories=[],this.c_screenname=null,angular.extend(this,b),this.$$fullname||(this.$$fullname=this.$fullname()),this.$$email||(this.$$email=this.$preferredEmail(c)),this.$$image||(this.$$image=this.image),this.$$image||a.$Preferences.avatar(this.$$email,32,{no_404:!0}).then(function(a){d.$$image=a}),this.isgroup&&(this.c_component="vlist"),this.$avatarIcon=this.$isList()?"group":"person",b.notes&&b.notes.length?this.notes=_.map(b.notes,function(a){return{value:a}}):this.notes&&this.notes.length||(this.notes=[{value:""}]),angular.forEach(this.refs,function(b,c){b.email&&(b.emails=[{value:b.email}]),b.id=b.reference,d.refs[c]=new a(b)}),this.birthday&&a.$Preferences.ready().then(function(){var b=a.$Preferences.$mdDateLocaleProvider;d.birthday=d.birthday.parseDate(b,"%Y-%m-%d"),d.$birthday=b.formatDate(d.birthday)}),this.$loaded=angular.isDefined(this.c_name)?a.STATUS.LOADED:a.STATUS.NOT_LOADED,this.empty=" "},a.prototype.$id=function(){return this.$futureCardData.then(function(a){return a.id})},a.prototype.$isLoading=function(){return this.$loaded==a.STATUS.LOADING},a.prototype.$reload=function(){var b;return this.$futureCardData?this:(b=a.$$resource.fetch([this.pid,this.id].join("/"),"view"),this.$unwrap(b))},a.prototype.$save=function(){var b=this,c="saveAsContact";return"vlist"==this.c_component&&(c="saveAsList",_.forEach(this.refs,function(a){a.reference=a.id})),a.$$resource.save([this.pid,this.id||"_new_"].join("/"),this.$omit(),{action:c}).then(function(c){return b.birthday&&(b.$birthday=a.$Preferences.$mdDateLocaleProvider.formatDate(b.birthday)),b.$shadowData=b.$omit(!0),c})},a.prototype.$delete=function(b,c){return b?void(c>-1&&this[b].length>c?this[b].splice(c,1):delete this[b]):a.$$resource.remove([this.pid,this.id].join("/"))},a.prototype.export=function(){var b;return b=[this.id],a.$$resource.download(this.pid,"export",{uids:b},{type:"application/octet-stream"})},a.prototype.$fullname=function(a){var b,c,d=this.c_cn||"",e=a&&a.html;return 0===d.length&&(c=[],this.c_givenname&&this.c_givenname.length>0&&c.push(this.c_givenname),this.nickname&&this.nickname.length>0&&c.push((e?"":"")+this.nickname+(e?"":"")),this.c_sn&&this.c_sn.length>0&&c.push(this.c_sn),c.length>0?d=c.join(" "):this.c_org&&this.c_org.length>0?d=this.c_org:this.emails&&this.emails.length>0&&(b=_.find(this.emails,function(a){return""!==a.value}),b&&(d=b.value))),d},a.prototype.$description=function(){var a=[];return this.title&&a.push(this.title),this.role&&a.push(this.role),this.orgUnits&&this.orgUnits.length>0&&_.forEach(this.orgUnits,function(b){""!==b.value&&a.push(b.value)}),this.c_org&&a.push(this.c_org),this.description&&a.push(this.description),a.join(", ")},a.prototype.$preferredEmail=function(a){var b,c;return a&&(c=new RegExp(a,"i"),b=_.find(this.emails,function(a){return c.test(a.value)})),b?b=b.value:(b=_.find(this.emails,function(a){return"pref"==a.type}),b=b?b.value:this.emails&&this.emails.length?this.emails[0].value:this.c_mail&&this.c_mail.length?this.c_mail[0]:""),b},a.prototype.$shortFormat=function(a){var b=[this.$$fullname],c=this.$preferredEmail(a);return c&&c!=this.$$fullname&&b.push(" <"+c+">"),b.join(" ")},a.prototype.$isCard=function(){return"vcard"==this.c_component},a.prototype.$isList=function(a){var b=!a||!a.expandable||a.expandable&&!this.isgroup;return"vlist"==this.c_component&&b},a.prototype.$addOrgUnit=function(a){if(angular.isUndefined(this.orgUnits))this.orgUnits=[{value:a}];else{for(var b=0;b1?(b=this.$omit(),_.forEach(this.emails,function(d){var e=new a(angular.extend({},b,{emails:[d]}));c.push(e)}),c):[this]},a.prototype.$reset=function(){var a=this;angular.forEach(this,function(b,c){"constructor"!=c&&"$"!=c[0]&&delete a[c]}),this.init(this.$shadowData),this.$shadowData=this.$omit(!0)},a.prototype.$unwrap=function(b){var c=this;return this.$loaded=a.STATUS.DELAYED_LOADING,a.$timeout(function(){c.$loaded!=a.STATUS.LOADED&&(c.$loaded=a.STATUS.LOADING)},a.STATUS.DELAYED_MS),this.$futureCardData=b.then(function(b){return c.init(b),c.$loaded=a.STATUS.LOADED,c.$shadowData=c.$omit(!0),c}),this.$futureCardData},a.prototype.$omit=function(b){var c={};return angular.forEach(this,function(a,d){"refs"==d?c.refs=_.map(a,function(a){return a.$omit(b)}):"constructor"!=d&&"$"!=d[0]&&(b?c[d]=angular.copy(a):c[d]=a)}),b||(c.birthday?c.birthday=c.birthday.format(a.$Preferences.$mdDateLocaleProvider,"%Y-%m-%d"):c.birthday=""),this.notes&&(c.notes=_.map(this.notes,"value")),c},a.prototype.toString=function(){var a=this.id+" "+this.$$fullname;return this.$$email&&(a+=" <"+this.$$email+">"),"["+a+"]"}}(); +!function(){"use strict";function a(b){if("function"!=typeof b.then)if(this.init(b),this.name&&!this.id){var c=a.$$resource.create("createFolder",this.name);this.$unwrap(c),this.acls={objectEditor:1,objectCreator:1,objectEraser:1}}else this.id&&(this.$acl=new a.$$Acl("Contacts/"+this.id));else this.$unwrap(b)}a.$factory=["$q","$timeout","$log","sgSettings","sgAddressBook_PRELOAD","Resource","Card","Acl","Preferences",function(b,c,d,e,f,g,h,i,j){return angular.extend(a,{$q:b,$timeout:c,$log:d,PRELOAD:f,$$resource:new g(e.activeUser("folderURL")+"Contacts",e.activeUser()),$Card:h,$$Acl:i,$Preferences:j,$query:{search:"name_or_address",value:"",sort:"c_cn",asc:1},activeUser:e.activeUser(),selectedFolder:null,$refreshTimeout:null}),j.ready().then(function(){j.settings.Contact.SortingState&&(a.$query.sort=j.settings.Contact.SortingState[0],a.$query.asc=parseInt(j.settings.Contact.SortingState[1]))}),a}];try{angular.module("SOGo.ContactsUI")}catch(a){angular.module("SOGo.ContactsUI",["SOGo.Common","SOGo.PreferencesUI"])}angular.module("SOGo.ContactsUI").constant("sgAddressBook_PRELOAD",{LOOKAHEAD:50,SIZE:100}).factory("AddressBook",a.$factory),a.$filterAll=function(b,c,d){var e={search:b};return b?(angular.isUndefined(a.$cards)&&(a.$cards=[]),angular.extend(e,c),a.$$resource.fetch(null,"allContactSearch",e).then(function(c){var e,f,g,h=function(a){return this.id==a.id};for(e=d?_.filter(c.contacts,function(a){return _.isUndefined(_.find(d,_.bind(h,a)))}):c.contacts,g=a.$cards.length-1;g>=0;g--)f=a.$cards[g],_.isUndefined(_.find(e,_.bind(h,f)))&&a.$cards.splice(g,1);return _.forEach(e,function(c,d){if(_.isUndefined(_.find(a.$cards,_.bind(h,c)))){var e=new a.$Card(_.mapKeys(c,function(a,b){return b.toLowerCase()}),b);a.$cards.splice(d,0,e)}}),a.$log.debug(a.$cards),a.$cards})):(a.$cards=[],a.$q.when(a.$cards))},a.$add=function(a){var b,c,d;b=a.isSubscription?this.$subscriptions:this.$addressbooks,c=_.find(b,function(b){return"personal"==a.id||"personal"!=b.id&&1===b.name.localeCompare(a.name)}),d=c?_.indexOf(_.map(b,"id"),c.id):1,b.splice(d,0,a)},a.$findAll=function(b){var c=this;return b&&(this.$addressbooks=[],this.$subscriptions=[],this.$remotes=[],angular.forEach(b,function(b,d){var e=new a(b);e.isRemote?c.$remotes.push(e):e.isSubscription?c.$subscriptions.push(e):c.$addressbooks.push(e)})),_.union(this.$addressbooks,this.$subscriptions,this.$remotes)},a.$subscribe=function(b,c){var d=this;return a.$$resource.userResource(b).fetch(c,"subscribe").then(function(b){var c=new a(b);return _.isUndefined(_.find(d.$subscriptions,function(a){return a.id==b.id}))&&a.$add(c),c})},a.$reloadAll=function(){var b=this;return a.$$resource.fetch("addressbooksList").then(function(c){_.forEach(c.addressbooks,function(c){var d,e;d=c.isRemote?b.$remotes:c.owner!=a.activeUser.login?b.$subscriptions:b.$addressbooks,e=_.find(d,function(a){return a.id==c.id}),e&&e.init(c)})})},a.prototype.init=function(b,c){var d=this;this.$$cards||(this.$$cards=[]),this.idsMap={},this.$cards=[],angular.forEach(b,function(a,b){"headers"!=b&&"cards"!=b&&(d[b]=a)}),this.isOwned=a.activeUser.isSuperUser||this.owner==a.activeUser.login,this.isSubscription=!this.isRemote&&this.owner!=a.activeUser.login},a.prototype.$id=function(){return this.id?a.$q.when(this.id):this.$futureAddressBookData.then(function(a){return a.id})},a.prototype.getLength=function(){return this.$cards.length},a.prototype.getItemAtIndex=function(a){var b;return!this.$isLoading&&a>=0&&a0&&(f=a.$$resource.post(this.id,"headers",{ids:e}),this.$unwrapHeaders(f))}return j},a.prototype.hasSelectedCard=function(){return angular.isDefined(this.selectedCard)},a.prototype.isSelectedCard=function(a){return this.hasSelectedCard()&&this.selectedCard==a},a.prototype.$selectedCard=function(){var a=this;return _.find(this.$cards,function(b){return b.id==a.selectedCard})},a.prototype.$selectedCardIndex=function(){return _.indexOf(_.map(this.$cards,"id"),this.selectedCard)},a.prototype.$selectedCards=function(){return _.filter(this.$cards,function(a){return a.selected})},a.prototype.$selectedCount=function(){var a;return a=0,this.$cards&&(a=_.filter(this.$cards,function(a){return a.selected}).length),a},a.prototype.$startRefreshTimeout=function(){var b=this;a.$refreshTimeout&&a.$timeout.cancel(a.$refreshTimeout),a.$Preferences.ready().then(function(){var c=a.$Preferences.defaults.SOGoRefreshViewCheck;if(c&&"manually"!=c){var d=angular.bind(b,a.prototype.$reload);a.$refreshTimeout=a.$timeout(d,1e3*c.timeInterval())}})},a.prototype.$reload=function(){return this.$startRefreshTimeout(),this.$filter()},a.prototype.$filter=function(b,c,d){var e,f=this,g=c&&c.dry;return g?e=angular.copy(a.$query):(this.$isLoading=!0,e=a.$query,this.isRemote||(e.partial=1)),a.$Preferences.ready().then(function(){return c&&(angular.extend(e,c),g&&!b)?(f.$$cards=[],a.$q.when(f.$$cards)):(angular.isDefined(b)&&(e.value=b),f.$id().then(function(c){var h=a.$$resource.fetch(c,"view",e);return g?h.then(function(e){var g,h,i,j,k,l=f.$$cards,m=function(a){return this==a.id};for(e.headers&&(j=_.invokeMap(e.headers[0],"toLowerCase"),k=j.indexOf("id"),e.headers.splice(0,1)),g=d?_.filter(e.ids,function(a){return _.isUndefined(_.find(d,_.bind(m,a)))}):e.ids,i=l.length-1;i>=0;i--)h=l[i],_.isUndefined(_.find(g,_.bind(m,h.id)))&&l.splice(i,1);return _.forEach(g,function(d,e){if(_.isUndefined(_.find(l,_.bind(m,d)))){var f={pid:c,id:d},g=new a.$Card(f,b);l.splice(e,0,g)}}),_.forEach(g,function(a,b){var c,d;l[b].id!=a&&(c=_.findIndex(l,_.bind(m,a)),d=l.splice(c,1),l.splice(b,0,d[0]))}),_.forEach(e.headers,function(a){var c,d=_.findIndex(l,_.bind(m,a[k]));d>-1&&(c=_.zipObject(j,a),l[d].init(c,b))}),l}):f.$unwrap(h)}))})},a.prototype.$rename=function(b){var c,d;return d=this.isSubscription?a.$subscriptions:a.$addressbooks,c=_.indexOf(_.map(d,"id"),this.id),this.name=b,d.splice(c,1),a.$add(this),this.$save()},a.prototype.$delete=function(){var b,c,d=this,e=a.$q.defer();return this.isSubscription?(c=a.$$resource.fetch(this.id,"unsubscribe"),b=a.$subscriptions):(c=a.$$resource.remove(this.id),b=a.$addressbooks),c.then(function(){var a=_.indexOf(_.map(b,"id"),d.id);b.splice(a,1),e.resolve()},e.reject),e.promise},a.prototype.$_deleteCards=function(a){var b=this;_.forEachRight(this.$cards,function(c,d){var e=_.findIndex(a,function(a){return c.id==a});e>-1?(a.splice(e,1),delete b.idsMap[c.id],b.isSelectedCard(c.id)&&delete b.selectedCard,b.$cards.splice(d,1)):b.idsMap[c.id]-=a.length})},a.prototype.$deleteCards=function(b){var c=this,d=_.map(b,"id");return a.$$resource.post(this.id,"batchDelete",{uids:d}).then(function(){c.$_deleteCards(d)})},a.prototype.$copyCards=function(b,c){var d=_.map(b,"id");return a.$$resource.post(this.id,"copy",{uids:d,folder:c})},a.prototype.$moveCards=function(b,c){var d,e=this;return d=_.map(b,"id"),a.$$resource.post(this.id,"move",{uids:d,folder:c}).then(function(){return e.$_deleteCards(d)})},a.prototype.$save=function(){return a.$$resource.save(this.id,this.$omit()).then(function(a){return a})},a.prototype.exportCards=function(b){var c;if(b){var d=_.filter(this.$cards,function(a){return a.selected});c=_.map(d,"id")}return a.$$resource.download(this.id,"export",angular.isDefined(c)?{uids:c}:null,{type:"application/octet-stream"})},a.prototype.$unwrap=function(b){var c=this;this.$isLoading=!0,this.$futureAddressBookData=b.then(function(b){return a.$timeout(function(){var d;return(!b.ids||c.$topIndex>b.ids.length-1)&&(c.$topIndex=0),angular.forEach(a.$findAll(),function(a,d){a.id==b.id&&angular.extend(c,a)}),c.init(b),c.ids&&(a.$log.debug("unwrapping "+c.ids.length+" cards"),_.reduce(c.ids,function(b,d,e){var f={pid:c.id,id:d};return c.idsMap[f.id]=e,b.push(new a.$Card(f)),b},c.$cards)),b.headers&&(d=_.invokeMap(b.headers[0],"toLowerCase"),b.headers.splice(0,1),c.ids?_.forEach(b.headers,function(a){var b=_.zipObject(d,a),e=c.idsMap[b.id];c.$cards[e].init(b)}):(c.$cards=[],angular.forEach(b.headers,function(b){var e=_.zipObject(d,b);angular.extend(e,{pid:c.id}),c.$cards.push(new a.$Card(e))}))),c.$acl=new a.$$Acl("Contacts/"+c.id),c.$startRefreshTimeout(),c.$isLoading=!1,a.$log.debug("addressbook "+c.id+" ready"),c})},function(b){c.isError=!0,angular.isObject(b)&&a.$timeout(function(){angular.extend(c,b)})})},a.prototype.$unwrapHeaders=function(b){var c=this;b.then(function(b){a.$timeout(function(){var a,d;b.length>0&&(a=_.invokeMap(b[0],"toLowerCase"),b.splice(0,1),_.forEach(b,function(b){b=_.zipObject(a,b),d=c.idsMap[b.id],angular.isDefined(d)&&c.$cards[d].init(b)}))})})},a.prototype.$omit=function(){var a={};return angular.forEach(this,function(b,c){"constructor"!=c&&"acls"!=c&&"ids"!=c&&"idsMap"!=c&&"urls"!=c&&"$"!=c[0]&&(a[c]=b)}),a}}(),function(){"use strict";function a(b,c){if("function"!=typeof b.then){if(this.init(b,c),this.pid&&!this.id){var d=a.$$resource.newguid(this.pid);this.$unwrap(d),this.isNew=!0}}else this.$unwrap(b)}a.$TEL_TYPES=["work","home","cell","fax","pager"],a.$EMAIL_TYPES=["work","home","pref"],a.$URL_TYPES=["work","home","pref"],a.$ADDRESS_TYPES=["work","home"],a.$factory=["$timeout","sgSettings","sgCard_STATUS","Resource","Preferences",function(b,c,d,e,f){return angular.extend(a,{STATUS:d,$$resource:new e(c.activeUser("folderURL")+"Contacts",c.activeUser()),$timeout:b,$Preferences:f}),f.ready().then(function(){f.defaults.SOGoContactsCategories&&(a.$categories=f.defaults.SOGoContactsCategories),f.defaults.SOGoAlternateAvatar&&(a.$alternateAvatar=f.defaults.SOGoAlternateAvatar)}),a}];try{angular.module("SOGo.ContactsUI")}catch(a){angular.module("SOGo.ContactsUI",["SOGo.Common","SOGo.PreferencesUI"])}angular.module("SOGo.ContactsUI").constant("sgCard_STATUS",{NOT_LOADED:0,DELAYED_LOADING:1,LOADING:2,LOADED:3,DELAYED_MS:300}).factory("Card",a.$factory),a.$find=function(b,c){var d=this.$$resource.fetch([b,c].join("/"),"view");return c?new a(d):a.$unwrapCollection(d)},a.filterCategories=function(b){var c=new RegExp(b,"i");return _.map(_.filter(a.$categories,function(a){return a.search(c)!=-1}),function(a){return{value:a}})},a.$unwrapCollection=function(b){var c={};return c.$futureCardData=b,b.then(function(b){a.$timeout(function(){angular.forEach(b,function(b,d){c[b.id]=new a(b)})})}),c},a.prototype.init=function(b,c){var d=this;this.refs=[],this.categories=[],this.c_screenname=null,angular.extend(this,b),this.$$fullname||(this.$$fullname=this.$fullname()),this.$$email||(this.$$email=this.$preferredEmail(c)),this.$$image||(this.$$image=this.image),this.$$image||a.$Preferences.avatar(this.$$email,32,{no_404:!0}).then(function(a){d.$$image=a}),this.isgroup&&(this.c_component="vlist"),this.$avatarIcon=this.$isList()?"group":"person",b.notes&&b.notes.length?this.notes=_.map(b.notes,function(a){return{value:a}}):this.notes&&this.notes.length||(this.notes=[{value:""}]),angular.forEach(this.refs,function(b,c){b.email&&(b.emails=[{value:b.email}]),b.id=b.reference,d.refs[c]=new a(b)}),this.birthday&&a.$Preferences.ready().then(function(){var b=a.$Preferences.$mdDateLocaleProvider;d.birthday=d.birthday.parseDate(b,"%Y-%m-%d"),d.$birthday=b.formatDate(d.birthday)}),this.$loaded=angular.isDefined(this.c_name)?a.STATUS.LOADED:a.STATUS.NOT_LOADED,this.empty=" "},a.prototype.$id=function(){return this.$futureCardData.then(function(a){return a.id})},a.prototype.$isLoading=function(){return this.$loaded==a.STATUS.LOADING},a.prototype.$reload=function(){var b;return this.$futureCardData?this:(b=a.$$resource.fetch([this.pid,this.id].join("/"),"view"),this.$unwrap(b))},a.prototype.$save=function(){var b=this,c="saveAsContact";return"vlist"==this.c_component&&(c="saveAsList",_.forEach(this.refs,function(a){a.reference=a.id})),a.$$resource.save([this.pid,this.id||"_new_"].join("/"),this.$omit(),{action:c}).then(function(c){return b.birthday&&(b.$birthday=a.$Preferences.$mdDateLocaleProvider.formatDate(b.birthday)),b.$shadowData=b.$omit(!0),c})},a.prototype.$delete=function(b,c){return b?void(c>-1&&this[b].length>c?this[b].splice(c,1):delete this[b]):a.$$resource.remove([this.pid,this.id].join("/"))},a.prototype.export=function(){var b;return b=[this.id],a.$$resource.download(this.pid,"export",{uids:b},{type:"application/octet-stream"})},a.prototype.$fullname=function(a){var b,c,d=this.c_cn||"",e=a&&a.html;return 0===d.length&&(c=[],this.c_givenname&&this.c_givenname.length>0&&c.push(this.c_givenname),this.nickname&&this.nickname.length>0&&c.push((e?"":"")+this.nickname+(e?"":"")),this.c_sn&&this.c_sn.length>0&&c.push(this.c_sn),c.length>0?d=c.join(" "):this.c_org&&this.c_org.length>0?d=this.c_org:this.emails&&this.emails.length>0&&(b=_.find(this.emails,function(a){return""!==a.value}),b&&(d=b.value))),d},a.prototype.$description=function(){var a=[];return this.title&&a.push(this.title),this.role&&a.push(this.role),this.orgUnits&&this.orgUnits.length>0&&_.forEach(this.orgUnits,function(b){""!==b.value&&a.push(b.value)}),this.c_org&&a.push(this.c_org),this.description&&a.push(this.description),a.join(", ")},a.prototype.$preferredEmail=function(a){var b,c;return a&&(c=new RegExp(a,"i"),b=_.find(this.emails,function(a){return c.test(a.value)})),b?b=b.value:(b=_.find(this.emails,function(a){return"pref"==a.type}),b=b?b.value:this.emails&&this.emails.length?this.emails[0].value:this.c_mail&&this.c_mail.length?this.c_mail[0]:""),b},a.prototype.$shortFormat=function(a){var b=[this.$$fullname],c=this.$preferredEmail(a);return c&&c!=this.$$fullname&&b.push(" <"+c+">"),b.join(" ")},a.prototype.$isCard=function(){return"vcard"==this.c_component},a.prototype.$isList=function(a){var b=!a||!a.expandable||a.expandable&&!this.isgroup;return"vlist"==this.c_component&&b},a.prototype.$addOrgUnit=function(a){if(angular.isUndefined(this.orgUnits))this.orgUnits=[{value:a}];else{for(var b=0;b1?(b=this.$omit(),_.forEach(this.emails,function(d){var e=new a(angular.extend({},b,{emails:[d]}));c.push(e)}),c):[this]},a.prototype.$reset=function(){var a=this;angular.forEach(this,function(b,c){"constructor"!=c&&"$"!=c[0]&&delete a[c]}),this.init(this.$shadowData),this.$shadowData=this.$omit(!0)},a.prototype.$unwrap=function(b){var c=this;return this.$loaded=a.STATUS.DELAYED_LOADING,a.$timeout(function(){c.$loaded!=a.STATUS.LOADED&&(c.$loaded=a.STATUS.LOADING)},a.STATUS.DELAYED_MS),this.$futureCardData=b.then(function(b){return c.init(b),c.$loaded=a.STATUS.LOADED,c.$shadowData=c.$omit(!0),c}),this.$futureCardData},a.prototype.$omit=function(b){var c={};return angular.forEach(this,function(a,d){"refs"==d?c.refs=_.map(a,function(a){return a.$omit(b)}):"constructor"!=d&&"$"!=d[0]&&(b?c[d]=angular.copy(a):c[d]=a)}),b||(c.birthday?c.birthday=c.birthday.format(a.$Preferences.$mdDateLocaleProvider,"%Y-%m-%d"):c.birthday=""),this.notes&&(c.notes=_.map(this.notes,"value")),c},a.prototype.toString=function(){var a=this.id+" "+this.$$fullname;return this.$$email&&(a+=" <"+this.$$email+">"),"["+a+"]"}}(); //# sourceMappingURL=Contacts.services.js.map \ No newline at end of file diff --git a/UI/WebServerResources/js/Contacts.services.js.map b/UI/WebServerResources/js/Contacts.services.js.map index 03eff1a41..f36f644c8 100644 --- a/UI/WebServerResources/js/Contacts.services.js.map +++ b/UI/WebServerResources/js/Contacts.services.js.map @@ -1 +1 @@ -{"version":3,"sources":["Contacts/AddressBook.service.js","Contacts/Card.service.js"],"names":["AddressBook","futureAddressBookData","then","this","init","name","id","newAddressBookData","$$resource","create","$unwrap","acls","objectEditor","objectCreator","objectEraser","$acl","$$Acl","$factory","$q","$timeout","$log","Settings","AddressBook_PRELOAD","Resource","Card","Acl","Preferences","angular","extend","PRELOAD","activeUser","$Card","$Preferences","$query","search","value","sort","asc","selectedFolder","$refreshTimeout","ready","settings","Contact","SortingState","parseInt","module","e","constant","LOOKAHEAD","SIZE","factory","$filterAll","options","excludedCards","params","isUndefined","$cards","fetch","response","results","card","index","compareIds","data","_","filter","contacts","find","bind","length","splice","forEach","mapKeys","key","toLowerCase","debug","when","$add","addressbook","list","sibling","i","isSubscription","$subscriptions","$addressbooks","o","localeCompare","indexOf","map","$findAll","_this","$remotes","isRemote","push","union","$subscribe","uid","path","userResource","addressbookData","$reloadAll","addressbooks","group","owner","login","prototype","$$cards","idsMap","isOwned","isSuperUser","$id","$futureAddressBookData","getLength","getItemAtIndex","$isLoading","$lastVisibleIndex","Math","max","$loadCard","endIndex","ids","futureHeadersData","cardId","startIndex","loaded","isDefined","$loaded","STATUS","NOT_LOADED","min","LOADED","LOADING","join","post","$unwrapHeaders","hasSelectedCard","selectedCard","isSelectedCard","$selectedCard","$selectedCardIndex","$selectedCards","selected","$selectedCount","count","$startRefreshTimeout","cancel","refreshViewCheck","defaults","SOGoRefreshViewCheck","f","$reload","timeInterval","$filter","query","dry","copy","partial","addressbookId","futureData","fields","idFieldIndex","cards","headers","invokeMap","pid","oldIndex","removedCards","findIndex","zipObject","$rename","$save","$delete","promise","d","defer","remove","resolve","reject","$_deleteCards","forEachRight","selectedIndex","$deleteCards","uids","$copyCards","folder","$moveCards","save","$omit","exportCards","selectedOnly","selectedUIDs","selectedCards","download","type","$topIndex","reduce","isError","isObject","j","cardHeaders","futureCardData","newCardData","newguid","isNew","$TEL_TYPES","$EMAIL_TYPES","$URL_TYPES","$ADDRESS_TYPES","Card_STATUS","SOGoContactsCategories","$categories","SOGoAlternateAvatar","$alternateAvatar","DELAYED_LOADING","DELAYED_MS","$find","$unwrapCollection","filterCategories","re","RegExp","category","collection","$futureCardData","refs","categories","c_screenname","$$fullname","$fullname","$$email","$preferredEmail","$$image","image","avatar","no_404","url","isgroup","c_component","$avatarIcon","$isList","notes","note","email","emails","reference","birthday","dlp","$mdDateLocaleProvider","parseDate","$birthday","formatDate","c_name","empty","action","ref","$shadowData","attribute","export","selectedIDs","names","fn","c_cn","html","c_givenname","nickname","c_sn","c_org","$description","description","title","role","orgUnits","unit","test","c_mail","$shortFormat","fullname","$isCard","condition","expandable","$addOrgUnit","orgUnit","$addEmail","$addScreenName","screenName","$addPhone","phones","$addUrl","urls","$addAddress","postoffice","street","street2","locality","region","country","postalcode","addresses","$addMember","explode","$reset","deep","format","toString","desc"],"mappings":"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","file":"Contacts.services.js","sourcesContent":["/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n\n(function() {\n 'use strict';\n\n /**\n * @name AddressBook\n * @constructor\n * @param {object} futureAddressBookData - either an object literal or a promise\n */\n function AddressBook(futureAddressBookData) {\n // Data is immediately available\n if (typeof futureAddressBookData.then !== 'function') {\n this.init(futureAddressBookData);\n if (this.name && !this.id) {\n // Create a new addressbook on the server\n var newAddressBookData = AddressBook.$$resource.create('createFolder', this.name);\n this.$unwrap(newAddressBookData);\n this.acls = {'objectEditor': 1, 'objectCreator': 1, 'objectEraser': 1};\n }\n else if (this.id) {\n this.$acl = new AddressBook.$$Acl('Contacts/' + this.id);\n }\n }\n else {\n // The promise will be unwrapped first\n this.$unwrap(futureAddressBookData);\n }\n }\n\n /**\n * @memberof AddressBook\n * @desc The factory we'll use to register with Angular\n * @returns the AddressBook constructor\n */\n AddressBook.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgAddressBook_PRELOAD', 'Resource', 'Card', 'Acl', 'Preferences', function($q, $timeout, $log, Settings, AddressBook_PRELOAD, Resource, Card, Acl, Preferences) {\n angular.extend(AddressBook, {\n $q: $q,\n $timeout: $timeout,\n $log: $log,\n PRELOAD: AddressBook_PRELOAD,\n $$resource: new Resource(Settings.activeUser('folderURL') + 'Contacts', Settings.activeUser()),\n $Card: Card,\n $$Acl: Acl,\n $Preferences: Preferences,\n $query: {search: 'name_or_address', value: '', sort: 'c_cn', asc: 1},\n activeUser: Settings.activeUser(),\n selectedFolder: null,\n $refreshTimeout: null\n });\n // Initialize sort parameters from user's settings\n Preferences.ready().then(function() {\n if (Preferences.settings.Contact.SortingState) {\n AddressBook.$query.sort = Preferences.settings.Contact.SortingState[0];\n AddressBook.$query.asc = parseInt(Preferences.settings.Contact.SortingState[1]);\n }\n });\n return AddressBook; // return constructor\n }];\n\n /**\n * @module SOGo.ContactsUI\n * @desc Factory registration of AddressBook in Angular module.\n */\n try {\n angular.module('SOGo.ContactsUI');\n }\n catch(e) {\n angular.module('SOGo.ContactsUI', ['SOGo.Common', 'SOGo.PreferencesUI']);\n }\n angular.module('SOGo.ContactsUI')\n .constant('sgAddressBook_PRELOAD', {\n LOOKAHEAD: 50,\n SIZE: 100\n })\n .factory('AddressBook', AddressBook.$factory);\n\n /**\n * @memberof AddressBook\n * @desc Search for cards among all addressbooks matching some criterias.\n * @param {string} search - the search string to match\n * @param {object} [options] - additional options to the query (excludeGroups and excludeLists)\n * @param {object[]} excludedCards - a list of Card objects that must be excluded from the results\n * @returns a collection of Cards instances\n */\n AddressBook.$filterAll = function(search, options, excludedCards) {\n var params = { search: search };\n\n if (!search) {\n // No query specified\n AddressBook.$cards = [];\n return AddressBook.$q.when(AddressBook.$cards);\n }\n if (angular.isUndefined(AddressBook.$cards)) {\n // First session query\n AddressBook.$cards = [];\n }\n\n angular.extend(params, options);\n\n return AddressBook.$$resource.fetch(null, 'allContactSearch', params).then(function(response) {\n var results, card, index,\n compareIds = function(data) {\n return this.id == data.id;\n };\n if (excludedCards) {\n // Remove excluded cards from results\n results = _.filter(response.contacts, function(data) {\n return _.isUndefined(_.find(excludedCards, _.bind(compareIds, data)));\n });\n }\n else {\n results = response.contacts;\n }\n // Remove cards that no longer match the search query\n for (index = AddressBook.$cards.length - 1; index >= 0; index--) {\n card = AddressBook.$cards[index];\n if (_.isUndefined(_.find(results, _.bind(compareIds, card)))) {\n AddressBook.$cards.splice(index, 1);\n }\n }\n // Add new cards matching the search query\n _.forEach(results, function(data, index) {\n if (_.isUndefined(_.find(AddressBook.$cards, _.bind(compareIds, data)))) {\n var card = new AddressBook.$Card(_.mapKeys(data, function(value, key) {\n return key.toLowerCase();\n }), search);\n AddressBook.$cards.splice(index, 0, card);\n }\n });\n AddressBook.$log.debug(AddressBook.$cards);\n return AddressBook.$cards;\n });\n };\n\n /**\n * @memberof AddressBook\n * @desc Add a new addressbook to the static list of addressbooks\n * @param {AddressBook} addressbook - an Addressbook object instance\n */\n AddressBook.$add = function(addressbook) {\n // Insert new addressbook at proper index\n var list, sibling, i;\n\n list = addressbook.isSubscription? this.$subscriptions : this.$addressbooks;\n sibling = _.find(list, function(o) {\n return (addressbook.id == 'personal' ||\n (o.id != 'personal' &&\n o.name.localeCompare(addressbook.name) === 1));\n });\n i = sibling ? _.indexOf(_.map(list, 'id'), sibling.id) : 1;\n list.splice(i, 0, addressbook);\n };\n\n /**\n * @memberof AddressBook\n * @desc Set or get the list of addressbooks. Will instantiate a new AddressBook object for each item.\n * @param {array} [data] - the metadata of the addressbooks\n * @returns the list of addressbooks\n */\n AddressBook.$findAll = function(data) {\n var _this = this;\n if (data) {\n this.$addressbooks = [];\n this.$subscriptions = [];\n this.$remotes = [];\n // Instanciate AddressBook objects\n angular.forEach(data, function(o, i) {\n var addressbook = new AddressBook(o);\n if (addressbook.isRemote)\n _this.$remotes.push(addressbook);\n else if (addressbook.isSubscription)\n _this.$subscriptions.push(addressbook);\n else\n _this.$addressbooks.push(addressbook);\n });\n }\n return _.union(this.$addressbooks, this.$subscriptions, this.$remotes);\n };\n\n /**\n * @memberOf AddressBook\n * @desc Subscribe to another user's addressbook and add it to the list of addressbooks.\n * @param {string} uid - user id\n * @param {string} path - path of folder for specified user\n * @returns a promise of the HTTP query result\n */\n AddressBook.$subscribe = function(uid, path) {\n var _this = this;\n return AddressBook.$$resource.userResource(uid).fetch(path, 'subscribe').then(function(addressbookData) {\n var addressbook = new AddressBook(addressbookData);\n if (_.isUndefined(_.find(_this.$subscriptions, function(o) {\n return o.id == addressbookData.id;\n }))) {\n // Not already subscribed\n AddressBook.$add(addressbook);\n }\n return addressbook;\n });\n };\n\n /**\n * @memberof AddressBook\n * @desc Reload the list of known addressbooks.\n */\n AddressBook.$reloadAll = function() {\n var _this = this;\n\n return AddressBook.$$resource.fetch('addressbooksList').then(function(data) {\n _.forEach(data.addressbooks, function(addressbookData) {\n var group, addressbook;\n\n if (addressbookData.isRemote)\n group = _this.$remotes;\n else if (addressbookData.owner != AddressBook.activeUser.login)\n group = _this.$subscriptions;\n else\n group = _this.$addressbooks;\n\n addressbook = _.find(group, function(o) { return o.id == addressbookData.id; });\n if (addressbook)\n addressbook.init(addressbookData);\n });\n });\n };\n\n /**\n * @function init\n * @memberof AddressBook.prototype\n * @desc Extend instance with new data and compute additional attributes.\n * @param {object} data - attributes of addressbook\n */\n AddressBook.prototype.init = function(data, options) {\n var _this = this;\n if (!this.$$cards) {\n // Array of cards for \"dry\" searches (see $filter)\n this.$$cards = [];\n }\n this.idsMap = {};\n this.$cards = [];\n // Extend instance with all attributes of data except headers\n angular.forEach(data, function(value, key) {\n if (key != 'headers' && key != 'cards') {\n _this[key] = value;\n }\n });\n // Add 'isOwned' and 'isSubscription' attributes based on active user (TODO: add it server-side?)\n this.isOwned = AddressBook.activeUser.isSuperUser || this.owner == AddressBook.activeUser.login;\n this.isSubscription = !this.isRemote && this.owner != AddressBook.activeUser.login;\n };\n\n /**\n * @function $id\n * @memberof AddressBook.prototype\n * @desc Resolve the addressbook id.\n * @returns a promise of the addressbook id\n */\n AddressBook.prototype.$id = function() {\n if (this.id) {\n // Object already unwrapped\n return AddressBook.$q.when(this.id);\n }\n else {\n // Wait until object is unwrapped\n return this.$futureAddressBookData.then(function(addressbook) {\n return addressbook.id;\n });\n }\n };\n\n /**\n * @function getLength\n * @memberof AddressBook.prototype\n * @desc Used by md-virtual-repeat / md-on-demand\n * @returns the number of cards in the addressbook\n */\n AddressBook.prototype.getLength = function() {\n return this.$cards.length;\n };\n\n /**\n * @function getItemAtIndex\n * @memberof AddressBook.prototype\n * @desc Used by md-virtual-repeat / md-on-demand\n * @returns the card at the specified index\n */\n AddressBook.prototype.getItemAtIndex = function(index) {\n var card;\n\n if (!this.$isLoading && index >= 0 && index < this.$cards.length) {\n card = this.$cards[index];\n this.$lastVisibleIndex = Math.max(0, index - 3); // Magic number is NUM_EXTRA from virtual-repeater.js\n\n if (this.$loadCard(card))\n return card;\n }\n return null;\n };\n\n /**\n * @function $loadCard\n * @memberof AddressBook.prototype\n * @desc Check if the card is loaded and in any case, fetch more cards headers from the server.\n * @returns true if the card metadata are already fetched\n */\n AddressBook.prototype.$loadCard = function(card) {\n var cardId = card.id,\n startIndex = this.idsMap[cardId],\n endIndex,\n index,\n max = this.$cards.length,\n loaded = false,\n ids,\n futureHeadersData;\n\n if (angular.isUndefined(this.ids) && card.id) {\n loaded = true;\n }\n else if (angular.isDefined(startIndex) && startIndex < this.$cards.length) {\n // Index is valid\n if (card.$loaded != AddressBook.$Card.STATUS.NOT_LOADED) {\n // Card headers are loaded or data is coming\n loaded = true;\n }\n\n // Preload more headers if possible\n endIndex = Math.min(startIndex + AddressBook.PRELOAD.LOOKAHEAD, max - 1);\n if (this.$cards[endIndex].$loaded != AddressBook.$Card.STATUS.NOT_LOADED) {\n index = Math.max(startIndex - AddressBook.PRELOAD.LOOKAHEAD, 0);\n if (this.$cards[index].$loaded != AddressBook.$Card.STATUS.LOADED) {\n // Previous cards not loaded; preload more headers further up\n endIndex = startIndex;\n startIndex = Math.max(startIndex - AddressBook.PRELOAD.SIZE, 0);\n }\n }\n else\n // Next cards not load; preload more headers further down\n endIndex = Math.min(startIndex + AddressBook.PRELOAD.SIZE, max - 1);\n\n if (this.$cards[startIndex].$loaded == AddressBook.$Card.STATUS.NOT_LOADED ||\n this.$cards[endIndex].$loaded == AddressBook.$Card.STATUS.NOT_LOADED) {\n\n for (ids = []; startIndex < endIndex && startIndex < max; startIndex++) {\n if (this.$cards[startIndex].$loaded != AddressBook.$Card.STATUS.NOT_LOADED) {\n // Card at this index is already loaded; increase the end index\n endIndex++;\n }\n else {\n // Card at this index will be loaded\n ids.push(this.$cards[startIndex].id);\n this.$cards[startIndex].$loaded = AddressBook.$Card.STATUS.LOADING;\n }\n }\n\n AddressBook.$log.debug('Loading Ids ' + ids.join(' ') + ' (' + ids.length + ' cards)');\n if (ids.length > 0) {\n futureHeadersData = AddressBook.$$resource.post(this.id, 'headers', {ids: ids});\n this.$unwrapHeaders(futureHeadersData);\n }\n }\n }\n return loaded;\n };\n\n /**\n * @function hasSelectedMessage\n * @memberof AddressBook.prototype\n * @desc Check if a card is selected.\n * @returns true if the a card is selected\n */\n AddressBook.prototype.hasSelectedCard = function() {\n return angular.isDefined(this.selectedCard);\n };\n\n /**\n * @function isSelectedCard\n * @memberof AddressBook.prototype\n * @desc Check if the specified card is selected.\n * @param {string} CardId\n * @returns true if the specified card is selected\n */\n AddressBook.prototype.isSelectedCard = function(cardId) {\n return this.hasSelectedCard() && this.selectedCard == cardId;\n };\n\n /**\n * @function $selectedCard\n * @memberof AddressBook.prototype\n * @desc Return the currently visible card.\n * @returns a Card instance or undefined if no card is displayed\n */\n AddressBook.prototype.$selectedCard = function() {\n var _this = this;\n\n return _.find(this.$cards, function(card) { return card.id == _this.selectedCard; });\n };\n\n /**\n * @function $selectedCardIndex\n * @memberof AddressBook.prototype\n * @desc Return the index of the currently visible card.\n * @returns a number or undefined if no card is selected\n */\n AddressBook.prototype.$selectedCardIndex = function() {\n return _.indexOf(_.map(this.$cards, 'id'), this.selectedCard);\n };\n\n /**\n * @function $selectedCards\n * @memberof AddressBook.prototype\n * @desc Return the cards selected by the user.\n * @returns Card instances\n */\n AddressBook.prototype.$selectedCards = function() {\n return _.filter(this.$cards, function(card) { return card.selected; });\n };\n\n /**\n * @function $selectedCount\n * @memberof AddressBook.prototype\n * @desc Return the number of cards selected by the user.\n * @returns the number of selected cards\n */\n AddressBook.prototype.$selectedCount = function() {\n var count;\n\n count = 0;\n if (this.$cards) {\n count = (_.filter(this.$cards, function(card) { return card.selected; })).length;\n }\n return count;\n };\n\n /**\n * @function $startRefreshTimeout\n * @memberof AddressBook.prototype\n * @desc Starts the refresh timeout for the current selected address book\n */\n AddressBook.prototype.$startRefreshTimeout = function() {\n var _this = this;\n\n if (AddressBook.$refreshTimeout)\n AddressBook.$timeout.cancel(AddressBook.$refreshTimeout);\n\n AddressBook.$Preferences.ready().then(function() {\n // Restart the refresh timer, if needed\n var refreshViewCheck = AddressBook.$Preferences.defaults.SOGoRefreshViewCheck;\n if (refreshViewCheck && refreshViewCheck != 'manually') {\n var f = angular.bind(_this, AddressBook.prototype.$reload);\n AddressBook.$refreshTimeout = AddressBook.$timeout(f, refreshViewCheck.timeInterval()*1000);\n }\n });\n };\n\n /**\n * @function $reload\n * @memberof AddressBook.prototype\n * @desc Reload list of cards\n * @returns a promise of the Cards instances\n */\n AddressBook.prototype.$reload = function() {\n var _this = this;\n\n this.$startRefreshTimeout();\n return this.$filter();\n };\n\n /**\n * @function $filter\n * @memberof AddressBook.prototype\n * @desc Search for cards matching some criterias\n * @param {string} search - the search string to match\n * @param {object} [options] - additional options to the query (dry, excludeList)\n * @returns a collection of Cards instances\n */\n AddressBook.prototype.$filter = function(search, options, excludedCards) {\n var _this = this, query,\n dry = options && options.dry;\n\n if (dry) {\n // Don't keep a copy of the query in dry mode\n query = angular.copy(AddressBook.$query);\n }\n else {\n this.$isLoading = true;\n query = AddressBook.$query;\n if (!this.isRemote) query.partial = 1;\n }\n\n return AddressBook.$Preferences.ready().then(function() {\n if (options) {\n angular.extend(query, options);\n if (dry) {\n if (!search) {\n // No query specified\n _this.$$cards = [];\n return AddressBook.$q.when(_this.$$cards);\n }\n }\n }\n\n if (angular.isDefined(search))\n query.value = search;\n\n return _this.$id().then(function(addressbookId) {\n var futureData = AddressBook.$$resource.fetch(addressbookId, 'view', query);\n\n if (dry) {\n return futureData.then(function(response) {\n var results, headers, card, index, fields, idFieldIndex,\n cards = _this.$$cards,\n compareIds = function(card) {\n return this == card.id;\n };\n\n if (response.headers) {\n // First entry of 'headers' are keys\n fields = _.invokeMap(response.headers[0], 'toLowerCase');\n idFieldIndex = fields.indexOf('id');\n response.headers.splice(0, 1);\n }\n\n if (excludedCards)\n // Remove excluded cards from results\n results = _.filter(response.ids, function(id) {\n return _.isUndefined(_.find(excludedCards, _.bind(compareIds, id)));\n });\n else\n results = response.ids;\n\n // Remove cards that no longer match the search query\n for (index = cards.length - 1; index >= 0; index--) {\n card = cards[index];\n if (_.isUndefined(_.find(results, _.bind(compareIds, card.id)))) {\n cards.splice(index, 1);\n }\n }\n\n // Add new cards matching the search query\n _.forEach(results, function(cardId, index) {\n if (_.isUndefined(_.find(cards, _.bind(compareIds, cardId)))) {\n var data = { pid: addressbookId, id: cardId };\n var card = new AddressBook.$Card(data, search);\n cards.splice(index, 0, card);\n }\n });\n\n // Respect the order of the results\n _.forEach(results, function(cardId, index) {\n var oldIndex, removedCards;\n if (cards[index].id != cardId) {\n oldIndex = _.findIndex(cards, _.bind(compareIds, cardId));\n removedCards = cards.splice(oldIndex, 1);\n cards.splice(index, 0, removedCards[0]);\n }\n });\n\n // Extend Card objects with received headers\n _.forEach(response.headers, function(data) {\n var card, index = _.findIndex(cards, _.bind(compareIds, data[idFieldIndex]));\n if (index > -1) {\n card = _.zipObject(fields, data);\n cards[index].init(card, search);\n }\n });\n\n return cards;\n });\n }\n else {\n // Unwrap promise and instantiate or extend Cards objets\n return _this.$unwrap(futureData);\n }\n });\n });\n };\n\n /**\n * @function $rename\n * @memberof AddressBook.prototype\n * @desc Rename the addressbook and keep the list sorted\n * @param {string} name - the new name\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.$rename = function(name) {\n var i = _.indexOf(_.map(AddressBook.$addressbooks, 'id'), this.id);\n this.name = name;\n AddressBook.$addressbooks.splice(i, 1);\n AddressBook.$add(this);\n return this.$save();\n };\n\n /**\n * @function $delete\n * @memberof AddressBook.prototype\n * @desc Delete the addressbook from the server and the static list of addressbooks.\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.$delete = function() {\n var _this = this,\n d = AddressBook.$q.defer(),\n list,\n promise;\n\n if (this.isSubscription) {\n promise = AddressBook.$$resource.fetch(this.id, 'unsubscribe');\n list = AddressBook.$subscriptions;\n }\n else {\n promise = AddressBook.$$resource.remove(this.id);\n list = AddressBook.$addressbooks;\n }\n\n promise.then(function() {\n var i = _.indexOf(_.map(list, 'id'), _this.id);\n list.splice(i, 1);\n d.resolve();\n }, d.reject);\n return d.promise;\n };\n\n /**\n * @function $_deleteCards\n * @memberof AddressBook.prototype\n * @desc Delete multiple cards from AddressBook object.\n * @param {string[]} ids - the cards ids\n */\n AddressBook.prototype.$_deleteCards = function(ids) {\n var _this = this;\n\n // Remove cards from $cards and idsMap\n _.forEachRight(this.$cards, function(card, index) {\n var selectedIndex = _.findIndex(ids, function(id) {\n return card.id == id;\n });\n if (selectedIndex > -1) {\n ids.splice(selectedIndex, 1);\n delete _this.idsMap[card.id];\n if (_this.isSelectedCard(card.id))\n delete _this.selectedCard;\n _this.$cards.splice(index, 1);\n }\n else {\n _this.idsMap[card.id] -= ids.length;\n }\n });\n };\n\n /**\n * @function $deleteCards\n * @memberof AddressBook.prototype\n * @desc Delete multiple cards from addressbook.\n * @return a promise of the HTTP operation\n */\n AddressBook.prototype.$deleteCards = function(cards) {\n var _this = this,\n ids = _.map(cards, 'id');\n \n return AddressBook.$$resource.post(this.id, 'batchDelete', {uids: ids}).then(function() {\n _this.$_deleteCards(ids);\n });\n };\n\n /**\n * @function $copyCards\n * @memberof AddressBook.prototype\n * @desc Copy multiple cards from addressbook to an other one.\n * @return a promise of the HTTP operation\n */\n AddressBook.prototype.$copyCards = function(cards, folder) {\n var uids = _.map(cards, 'id');\n return AddressBook.$$resource.post(this.id, 'copy', {uids: uids, folder: folder});\n };\n\n /**\n * @function $moveCards\n * @memberof AddressBook.prototype\n * @desc Move multiple cards from the current addressbook to a target one\n * @param {object[]} cards - instances of Card object\n * @param {string} folder - the destination folder id\n * @return a promise of the HTTP operation\n */\n AddressBook.prototype.$moveCards = function(cards, folder) {\n var _this = this, uids;\n\n uids = _.map(cards, 'id');\n return AddressBook.$$resource.post(this.id, 'move', {uids: uids, folder: folder})\n .then(function() {\n return _this.$_deleteCards(uids);\n });\n };\n\n /**\n * @function $save\n * @memberof AddressBook.prototype\n * @desc Save the addressbook to the server. This currently can only affect the name of the addressbook.\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.$save = function() {\n return AddressBook.$$resource.save(this.id, this.$omit()).then(function(data) {\n return data;\n });\n };\n\n /**\n * @function $exportCards\n * @memberof AddressBook.prototype\n * @desc Export the selected/all cards\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.exportCards = function(selectedOnly) {\n var selectedUIDs;\n\n if (selectedOnly) {\n var selectedCards = _.filter(this.$cards, function(card) { return card.selected; });\n selectedUIDs = _.map(selectedCards, 'id');\n }\n\n return AddressBook.$$resource.download(this.id, 'export', (angular.isDefined(selectedUIDs) ? {uids: selectedUIDs} : null), {type: 'application/octet-stream'});\n };\n\n /**\n * @function $unwrap\n * @memberof AddressBook.prototype\n * @desc Unwrap a promise and instanciate new Card objects using received data.\n * @param {promise} futureAddressBookData - a promise of the AddressBook's data\n */\n AddressBook.prototype.$unwrap = function(futureAddressBookData) {\n var _this = this;\n\n this.$isLoading = true;\n\n // Expose and resolve the promise\n this.$futureAddressBookData = futureAddressBookData.then(function(response) {\n return AddressBook.$timeout(function() {\n var headers;\n\n if (!response.ids || _this.$topIndex > response.ids.length - 1)\n _this.$topIndex = 0;\n\n // Extend AddressBook instance from data of addressbooks list.\n // Will inherit attributes such as isEditable and isRemote.\n angular.forEach(AddressBook.$findAll(), function(o, i) {\n if (o.id == response.id) {\n angular.extend(_this, o);\n }\n });\n\n // Extend AddressBook instance with received data\n _this.init(response);\n\n if (_this.ids) {\n AddressBook.$log.debug('unwrapping ' + _this.ids.length + ' cards');\n\n // Instanciate Card objects\n _.reduce(_this.ids, function(cards, card, i) {\n var data = { pid: _this.id, id: card };\n\n // Build map of ID <=> index\n _this.idsMap[data.id] = i;\n\n cards.push(new AddressBook.$Card(data));\n\n return cards;\n }, _this.$cards);\n }\n\n if (response.headers) {\n // First entry of 'headers' are keys\n headers = _.invokeMap(response.headers[0], 'toLowerCase');\n response.headers.splice(0, 1);\n\n if (_this.ids) {\n // Extend Card objects with received headers\n _.forEach(response.headers, function(data) {\n var o = _.zipObject(headers, data),\n i = _this.idsMap[o.id];\n _this.$cards[i].init(o);\n });\n }\n else {\n // Instanciate Card objects\n _this.$cards = [];\n angular.forEach(response.headers, function(data) {\n var o = _.zipObject(headers, data);\n angular.extend(o, { pid: _this.id });\n _this.$cards.push(new AddressBook.$Card(o));\n });\n }\n }\n\n // Instanciate Acl object\n _this.$acl = new AddressBook.$$Acl('Contacts/' + _this.id);\n\n _this.$startRefreshTimeout();\n\n _this.$isLoading = false;\n\n AddressBook.$log.debug('addressbook ' + _this.id + ' ready');\n\n return _this;\n });\n }, function(data) {\n _this.isError = true;\n if (angular.isObject(data)) {\n AddressBook.$timeout(function() {\n angular.extend(_this, data);\n });\n }\n });\n };\n\n /**\n * @function $unwrapHeaders\n * @memberof AddressBook.prototype\n * @desc Unwrap a promise and extend matching Card objects with received data.\n * @param {promise} futureHeadersData - a promise of the metadata of some cards\n */\n AddressBook.prototype.$unwrapHeaders = function(futureHeadersData) {\n var _this = this;\n\n futureHeadersData.then(function(data) {\n AddressBook.$timeout(function() {\n var headers, j;\n if (data.length > 0) {\n // First entry of 'headers' are keys\n headers = _.invokeMap(data[0], 'toLowerCase');\n data.splice(0, 1);\n _.forEach(data, function(cardHeaders) {\n cardHeaders = _.zipObject(headers, cardHeaders);\n j = _this.idsMap[cardHeaders.id];\n if (angular.isDefined(j)) {\n _this.$cards[j].init(cardHeaders);\n }\n });\n }\n });\n });\n };\n\n /**\n * @function $omit\n * @memberof AddressBook.prototype\n * @desc Return a sanitized object used to send to the server.\n * @return an object literal copy of the Addressbook instance\n */\n AddressBook.prototype.$omit = function() {\n var addressbook = {};\n angular.forEach(this, function(value, key) {\n if (key != 'constructor' &&\n key != 'ids' &&\n key[0] != '$') {\n addressbook[key] = value;\n }\n });\n return addressbook;\n };\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n\n(function() {\n 'use strict';\n\n /**\n * @name Card\n * @constructor\n * @param {object} futureCardData\n * @param {string} [partial]\n */\n function Card(futureCardData, partial) {\n\n // Data is immediately available\n if (typeof futureCardData.then !== 'function') {\n this.init(futureCardData, partial);\n if (this.pid && !this.id) {\n // Prepare for the creation of a new card;\n // Get UID from the server.\n var newCardData = Card.$$resource.newguid(this.pid);\n this.$unwrap(newCardData);\n this.isNew = true;\n }\n }\n else {\n // The promise will be unwrapped first\n this.$unwrap(futureCardData);\n }\n }\n\n Card.$TEL_TYPES = ['work', 'home', 'cell', 'fax', 'pager'];\n Card.$EMAIL_TYPES = ['work', 'home', 'pref'];\n Card.$URL_TYPES = ['work', 'home', 'pref'];\n Card.$ADDRESS_TYPES = ['work', 'home'];\n\n /**\n * @memberof Card\n * @desc The factory we'll use to register with Angular.\n * @returns the Card constructor\n */\n Card.$factory = ['$timeout', 'sgSettings', 'sgCard_STATUS', 'Resource', 'Preferences', function($timeout, Settings, Card_STATUS, Resource, Preferences) {\n angular.extend(Card, {\n STATUS: Card_STATUS,\n $$resource: new Resource(Settings.activeUser('folderURL') + 'Contacts', Settings.activeUser()),\n $timeout: $timeout,\n $Preferences: Preferences\n });\n // Initialize categories from user's defaults\n Preferences.ready().then(function() {\n if (Preferences.defaults.SOGoContactsCategories) {\n Card.$categories = Preferences.defaults.SOGoContactsCategories;\n }\n if (Preferences.defaults.SOGoAlternateAvatar)\n Card.$alternateAvatar = Preferences.defaults.SOGoAlternateAvatar;\n });\n\n return Card; // return constructor\n }];\n\n /**\n * @module SOGo.ContactsUI\n * @desc Factory registration of Card in Angular module.\n */\n try {\n angular.module('SOGo.ContactsUI');\n }\n catch(e) {\n angular.module('SOGo.ContactsUI', ['SOGo.Common', 'SOGo.PreferencesUI']);\n }\n angular.module('SOGo.ContactsUI')\n .constant('sgCard_STATUS', {\n NOT_LOADED: 0,\n DELAYED_LOADING: 1,\n LOADING: 2,\n LOADED: 3,\n DELAYED_MS: 300\n })\n .factory('Card', Card.$factory);\n\n /**\n * @memberof Card\n * @desc Fetch a card from a specific addressbook.\n * @param {string} addressbookId - the addressbook ID\n * @param {string} cardId - the card ID\n * @see {@link AddressBook.$getCard}\n */\n Card.$find = function(addressbookId, cardId) {\n var futureCardData = this.$$resource.fetch([addressbookId, cardId].join('/'), 'view');\n\n if (cardId) return new Card(futureCardData); // a single card\n\n return Card.$unwrapCollection(futureCardData); // a collection of cards\n };\n\n /**\n * @function filterCategories\n * @memberof Card.prototype\n * @desc Search for categories matching some criterias\n * @param {string} search - the search string to match\n * @returns a collection of strings\n */\n Card.filterCategories = function(query) {\n var re = new RegExp(query, 'i');\n return _.map(_.filter(Card.$categories, function(category) {\n return category.search(re) != -1;\n }), function(category) {\n return { value: category };\n });\n };\n\n /**\n * @memberof Card\n * @desc Unwrap to a collection of Card instances.\n * @param {object} futureCardData\n */\n Card.$unwrapCollection = function(futureCardData) {\n var collection = {};\n\n collection.$futureCardData = futureCardData;\n\n futureCardData.then(function(cards) {\n Card.$timeout(function() {\n angular.forEach(cards, function(data, index) {\n collection[data.id] = new Card(data);\n });\n });\n });\n\n return collection;\n };\n\n /**\n * @function init\n * @memberof Card.prototype\n * @desc Extend instance with required attributes and new data.\n * @param {object} data - attributes of card\n */\n Card.prototype.init = function(data, partial) {\n var _this = this;\n\n this.refs = [];\n this.categories = [];\n this.c_screenname = null;\n angular.extend(this, data);\n if (!this.$$fullname)\n this.$$fullname = this.$fullname();\n if (!this.$$email)\n this.$$email = this.$preferredEmail(partial);\n if (!this.$$image)\n this.$$image = this.image;\n if (!this.$$image)\n Card.$Preferences.avatar(this.$$email, 32, {no_404: true}).then(function(url) {\n _this.$$image = url;\n });\n if (this.isgroup)\n this.c_component = 'vlist';\n this.$avatarIcon = this.$isList()? 'group' : 'person';\n if (data.notes && data.notes.length)\n this.notes = _.map(data.notes, function(note) { return { 'value': note }; });\n else if (!this.notes || !this.notes.length)\n this.notes = [ { value: '' } ];\n // Instanciate Card objects for list members\n angular.forEach(this.refs, function(o, i) {\n if (o.email) o.emails = [{value: o.email}];\n o.id = o.reference;\n _this.refs[i] = new Card(o);\n });\n // Instanciate date object of birthday\n if (this.birthday) {\n Card.$Preferences.ready().then(function() {\n var dlp = Card.$Preferences.$mdDateLocaleProvider;\n _this.birthday = _this.birthday.parseDate(dlp, '%Y-%m-%d');\n _this.$birthday = dlp.formatDate(_this.birthday);\n });\n }\n\n this.$loaded = angular.isDefined(this.c_name)? Card.STATUS.LOADED : Card.STATUS.NOT_LOADED;\n\n // An empty attribute to trick md-autocomplete when adding attendees from the appointment editor\n this.empty = ' ';\n };\n\n /**\n * @function $id\n * @memberof Card.prototype\n * @desc Return the card ID.\n * @returns the card ID\n */\n Card.prototype.$id = function() {\n return this.$futureCardData.then(function(data) {\n return data.id;\n });\n };\n\n /**\n * @function $isLoading\n * @memberof Card.prototype\n * @returns true if the Card definition is still being retrieved from server after a specific delay\n * @see sgCard_STATUS\n */\n Card.prototype.$isLoading = function() {\n return this.$loaded == Card.STATUS.LOADING;\n };\n\n /**\n * @function $reload\n * @memberof Message.prototype\n * @desc Fetch the viewable message body along with other metadata such as the list of attachments.\n * @returns a promise of the HTTP operation\n */\n Card.prototype.$reload = function() {\n var futureCardData;\n\n if (this.$futureCardData)\n return this;\n\n futureCardData = Card.$$resource.fetch([this.pid, this.id].join('/'), 'view');\n\n return this.$unwrap(futureCardData);\n };\n\n /**\n * @function $save\n * @memberof Card.prototype\n * @desc Save the card to the server.\n */\n Card.prototype.$save = function() {\n var _this = this,\n action = 'saveAsContact';\n\n if (this.c_component == 'vlist') {\n action = 'saveAsList';\n _.forEach(this.refs, function(ref) {\n ref.reference = ref.id;\n });\n }\n\n return Card.$$resource.save([this.pid, this.id || '_new_'].join('/'),\n this.$omit(),\n { action: action })\n .then(function(data) {\n // Format birthdate\n if (_this.birthday)\n _this.$birthday = Card.$Preferences.$mdDateLocaleProvider.formatDate(_this.birthday);\n // Make a copy of the data for an eventual reset\n _this.$shadowData = _this.$omit(true);\n return data;\n });\n };\n\n Card.prototype.$delete = function(attribute, index) {\n if (attribute) {\n if (index > -1 && this[attribute].length > index) {\n this[attribute].splice(index, 1);\n }\n else\n delete this[attribute];\n }\n else {\n // No arguments -- delete card\n return Card.$$resource.remove([this.pid, this.id].join('/'));\n }\n };\n\n /**\n * @function export\n * @memberof Card.prototype\n * @desc Download the current card\n * @returns a promise of the HTTP operation\n */\n Card.prototype.export = function() {\n var selectedIDs;\n\n selectedIDs = [ this.id ];\n\n return Card.$$resource.download(this.pid, 'export', {uids: selectedIDs}, {type: 'application/octet-stream'});\n };\n\n Card.prototype.$fullname = function(options) {\n var fn = this.c_cn || '', html = options && options.html, email, names;\n if (fn.length === 0) {\n names = [];\n if (this.c_givenname && this.c_givenname.length > 0)\n names.push(this.c_givenname);\n if (this.nickname && this.nickname.length > 0)\n names.push((html?'':'') + this.nickname + (html?'':''));\n if (this.c_sn && this.c_sn.length > 0)\n names.push(this.c_sn);\n if (names.length > 0)\n fn = names.join(' ');\n else if (this.c_org && this.c_org.length > 0) {\n fn = this.c_org;\n }\n else if (this.emails && this.emails.length > 0) {\n email = _.find(this.emails, function(i) { return i.value !== ''; });\n if (email)\n fn = email.value;\n }\n }\n\n return fn;\n };\n\n Card.prototype.$description = function() {\n var description = [];\n if (this.title) description.push(this.title);\n if (this.role) description.push(this.role);\n if (this.orgUnits && this.orgUnits.length > 0)\n _.forEach(this.orgUnits, function(unit) {\n if (unit.value !== '')\n description.push(unit.value);\n });\n if (this.c_org) description.push(this.c_org);\n if (this.description) description.push(this.description);\n\n return description.join(', ');\n };\n\n /**\n * @function $preferredEmail\n * @memberof Card.prototype\n * @desc Get the preferred email address.\n * @param {string} [partial] - a partial string that the email must match\n * @returns the first email address of type \"pref\" or the first address if none found\n */\n Card.prototype.$preferredEmail = function(partial) {\n var email, re;\n if (partial) {\n re = new RegExp(partial, 'i');\n email = _.find(this.emails, function(o) {\n return re.test(o.value);\n });\n }\n if (email) {\n email = email.value;\n }\n else {\n email = _.find(this.emails, function(o) {\n return o.type == 'pref';\n });\n if (email) {\n email = email.value;\n }\n else if (this.emails && this.emails.length) {\n email = this.emails[0].value;\n }\n else if (this.c_mail && this.c_mail.length) {\n email = this.c_mail[0];\n }\n else {\n email = '';\n }\n }\n\n return email;\n };\n\n /**\n * @function $shortFormat\n * @memberof Card.prototype\n * @param {string} [partial] - a partial string that the email must match\n * @returns the fullname along with a matching email address in parentheses\n */\n Card.prototype.$shortFormat = function(partial) {\n var fullname = [this.$$fullname],\n email = this.$preferredEmail(partial);\n if (email && email != this.$$fullname)\n fullname.push(' <' + email + '>');\n return fullname.join(' ');\n };\n\n Card.prototype.$isCard = function() {\n return this.c_component == 'vcard';\n };\n\n Card.prototype.$isList = function(options) {\n // isGroup attribute means it's a group of a LDAP source (not expandable on the client-side)\n var condition = (!options || !options.expandable || options.expandable && !this.isgroup);\n return this.c_component == 'vlist' && condition;\n };\n\n Card.prototype.$addOrgUnit = function(orgUnit) {\n if (angular.isUndefined(this.orgUnits)) {\n this.orgUnits = [{value: orgUnit}];\n }\n else {\n for (var i = 0; i < this.orgUnits.length; i++) {\n if (this.orgUnits[i].value == orgUnit) {\n break;\n }\n }\n if (i == this.orgUnits.length)\n this.orgUnits.push({value: orgUnit});\n }\n return this.orgUnits.length - 1;\n };\n\n // Card.prototype.$addCategory = function(category) {\n // if (category) {\n // if (angular.isUndefined(this.categories)) {\n // this.categories = [{value: category}];\n // }\n // else {\n // for (var i = 0; i < this.categories.length; i++) {\n // if (this.categories[i].value == category) {\n // break;\n // }\n // }\n // if (i == this.categories.length)\n // this.categories.push({value: category});\n // }\n // }\n // };\n\n Card.prototype.$addEmail = function(type) {\n if (angular.isUndefined(this.emails)) {\n this.emails = [{type: type, value: ''}];\n }\n else if (_.isUndefined(_.find(this.emails, function(i) { return i.value === ''; }))) {\n this.emails.push({type: type, value: ''});\n }\n return this.emails.length - 1;\n };\n\n Card.prototype.$addScreenName = function(screenName) {\n this.c_screenname = screenName;\n };\n\n Card.prototype.$addPhone = function(type) {\n if (angular.isUndefined(this.phones)) {\n this.phones = [{type: type, value: ''}];\n }\n else if (_.isUndefined(_.find(this.phones, function(i) { return i.value === ''; }))) {\n this.phones.push({type: type, value: ''});\n }\n return this.phones.length - 1;\n };\n\n Card.prototype.$addUrl = function(type, url) {\n if (angular.isUndefined(this.urls)) {\n this.urls = [{type: type, value: url}];\n }\n else if (_.isUndefined(_.find(this.urls, function(i) { return i.value == url; }))) {\n this.urls.push({type: type, value: url});\n }\n return this.urls.length - 1;\n };\n\n Card.prototype.$addAddress = function(type, postoffice, street, street2, locality, region, country, postalcode) {\n if (angular.isUndefined(this.addresses)) {\n this.addresses = [{type: type, postoffice: postoffice, street: street, street2: street2, locality: locality, region: region, country: country, postalcode: postalcode}];\n }\n else if (!_.find(this.addresses, function(i) {\n return i.street == street &&\n i.street2 == street2 &&\n i.locality == locality &&\n i.country == country &&\n i.postalcode == postalcode;\n })) {\n this.addresses.push({type: type, postoffice: postoffice, street: street, street2: street2, locality: locality, region: region, country: country, postalcode: postalcode});\n }\n return this.addresses.length - 1;\n };\n\n Card.prototype.$addMember = function(email) {\n var card = new Card({email: email, emails: [{value: email}]}),\n i;\n if (angular.isUndefined(this.refs)) {\n this.refs = [card];\n }\n else if (email.length === 0) {\n this.refs.push(card);\n }\n else {\n for (i = 0; i < this.refs.length; i++) {\n if (this.refs[i].email == email) {\n break;\n }\n }\n if (i == this.refs.length)\n this.refs.push(card);\n }\n return this.refs.length - 1;\n };\n\n /**\n * @function explode\n * @memberof Card.prototype\n * @desc Create a new Card associated to each email address of this card.\n * @return an array of Card instances\n */\n Card.prototype.explode = function() {\n var _this = this, cards = [], data;\n\n if (this.emails.length > 1) {\n data = this.$omit();\n _.forEach(this.emails, function(email) {\n var card = new Card(angular.extend({}, data, {emails: [email]}));\n cards.push(card);\n });\n return cards;\n }\n else\n return [this];\n };\n\n /**\n * @function $reset\n * @memberof Card.prototype\n * @desc Reset the original state the card's data.\n */\n Card.prototype.$reset = function() {\n var _this = this;\n angular.forEach(this, function(value, key) {\n if (key != 'constructor' && key[0] != '$') {\n delete _this[key];\n }\n });\n this.init(this.$shadowData);\n this.$shadowData = this.$omit(true);\n };\n\n /**\n * @function $updateMember\n * @memberof Card.prototype\n * @desc Update an existing list member from a Card instance.\n * A list member has the following attribtues:\n * - email\n * - reference\n * - fn\n * @param {number} index\n * @param {string} email\n * @param {Card} card\n */\n // Card.prototype.$updateMember = function(index, email, card) {\n // var ref = {\n // email: email,\n // emails: [{value: email}],\n // reference: card.c_name,\n // c_cn: card.$fullname()\n // };\n // this.refs[index] = new Card(ref);\n // };\n\n /**\n * @function $unwrap\n * @memberof Card.prototype\n * @desc Unwrap a promise and make a copy of the resolved data.\n * @param {object} futureCardData - a promise of the Card's data\n */\n Card.prototype.$unwrap = function(futureCardData) {\n var _this = this;\n\n // Card is not loaded yet\n this.$loaded = Card.STATUS.DELAYED_LOADING;\n Card.$timeout(function() {\n if (_this.$loaded != Card.STATUS.LOADED)\n _this.$loaded = Card.STATUS.LOADING;\n }, Card.STATUS.DELAYED_MS);\n\n // Expose the promise\n this.$futureCardData = futureCardData.then(function(data) {\n _this.init(data);\n // Mark card as loaded\n _this.$loaded = Card.STATUS.LOADED;\n // Make a copy of the data for an eventual reset\n _this.$shadowData = _this.$omit(true);\n\n return _this;\n });\n\n return this.$futureCardData;\n };\n\n /**\n * @function $omit\n * @memberof Card.prototype\n * @desc Return a sanitized object used to send to the server.\n * @param {boolean} [deep] - make a deep copy if true\n * @return an object literal copy of the Card instance\n */\n Card.prototype.$omit = function(deep) {\n var card = {};\n angular.forEach(this, function(value, key) {\n if (key == 'refs') {\n card.refs = _.map(value, function(o) {\n return o.$omit(deep);\n });\n }\n else if (key != 'constructor' && key[0] != '$') {\n if (deep)\n card[key] = angular.copy(value);\n else\n card[key] = value;\n }\n });\n\n // We convert back our birthday object\n if (!deep) {\n if (card.birthday)\n card.birthday = card.birthday.format(Card.$Preferences.$mdDateLocaleProvider, '%Y-%m-%d');\n else\n card.birthday = '';\n }\n\n // We flatten the notes to an array of strings\n if (this.notes)\n card.notes = _.map(this.notes, 'value');\n\n return card;\n };\n\n Card.prototype.toString = function() {\n var desc = this.id + ' ' + this.$$fullname;\n\n if (this.$$email)\n desc += ' <' + this.$$email + '>';\n\n return '[' + desc + ']';\n };\n})();\n"]} \ No newline at end of file +{"version":3,"sources":["Contacts/AddressBook.service.js","Contacts/Card.service.js"],"names":["AddressBook","futureAddressBookData","then","this","init","name","id","newAddressBookData","$$resource","create","$unwrap","acls","objectEditor","objectCreator","objectEraser","$acl","$$Acl","$factory","$q","$timeout","$log","Settings","AddressBook_PRELOAD","Resource","Card","Acl","Preferences","angular","extend","PRELOAD","activeUser","$Card","$Preferences","$query","search","value","sort","asc","selectedFolder","$refreshTimeout","ready","settings","Contact","SortingState","parseInt","module","e","constant","LOOKAHEAD","SIZE","factory","$filterAll","options","excludedCards","params","isUndefined","$cards","fetch","response","results","card","index","compareIds","data","_","filter","contacts","find","bind","length","splice","forEach","mapKeys","key","toLowerCase","debug","when","$add","addressbook","list","sibling","i","isSubscription","$subscriptions","$addressbooks","o","localeCompare","indexOf","map","$findAll","_this","$remotes","isRemote","push","union","$subscribe","uid","path","userResource","addressbookData","$reloadAll","addressbooks","group","owner","login","prototype","$$cards","idsMap","isOwned","isSuperUser","$id","$futureAddressBookData","getLength","getItemAtIndex","$isLoading","$lastVisibleIndex","Math","max","$loadCard","endIndex","ids","futureHeadersData","cardId","startIndex","loaded","isDefined","$loaded","STATUS","NOT_LOADED","min","LOADED","LOADING","join","post","$unwrapHeaders","hasSelectedCard","selectedCard","isSelectedCard","$selectedCard","$selectedCardIndex","$selectedCards","selected","$selectedCount","count","$startRefreshTimeout","cancel","refreshViewCheck","defaults","SOGoRefreshViewCheck","f","$reload","timeInterval","$filter","query","dry","copy","partial","addressbookId","futureData","fields","idFieldIndex","cards","headers","invokeMap","pid","oldIndex","removedCards","findIndex","zipObject","$rename","$save","$delete","promise","d","defer","remove","resolve","reject","$_deleteCards","forEachRight","selectedIndex","$deleteCards","uids","$copyCards","folder","$moveCards","save","$omit","exportCards","selectedOnly","selectedUIDs","selectedCards","download","type","$topIndex","reduce","isError","isObject","j","cardHeaders","futureCardData","newCardData","newguid","isNew","$TEL_TYPES","$EMAIL_TYPES","$URL_TYPES","$ADDRESS_TYPES","Card_STATUS","SOGoContactsCategories","$categories","SOGoAlternateAvatar","$alternateAvatar","DELAYED_LOADING","DELAYED_MS","$find","$unwrapCollection","filterCategories","re","RegExp","category","collection","$futureCardData","refs","categories","c_screenname","$$fullname","$fullname","$$email","$preferredEmail","$$image","image","avatar","no_404","url","isgroup","c_component","$avatarIcon","$isList","notes","note","email","emails","reference","birthday","dlp","$mdDateLocaleProvider","parseDate","$birthday","formatDate","c_name","empty","action","ref","$shadowData","attribute","export","selectedIDs","names","fn","c_cn","html","c_givenname","nickname","c_sn","c_org","$description","description","title","role","orgUnits","unit","test","c_mail","$shortFormat","fullname","$isCard","condition","expandable","$addOrgUnit","orgUnit","$addEmail","$addScreenName","screenName","$addPhone","phones","$addUrl","urls","$addAddress","postoffice","street","street2","locality","region","country","postalcode","addresses","$addMember","explode","$reset","deep","format","toString","desc"],"mappings":"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","file":"Contacts.services.js","sourcesContent":["/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n\n(function() {\n 'use strict';\n\n /**\n * @name AddressBook\n * @constructor\n * @param {object} futureAddressBookData - either an object literal or a promise\n */\n function AddressBook(futureAddressBookData) {\n // Data is immediately available\n if (typeof futureAddressBookData.then !== 'function') {\n this.init(futureAddressBookData);\n if (this.name && !this.id) {\n // Create a new addressbook on the server\n var newAddressBookData = AddressBook.$$resource.create('createFolder', this.name);\n this.$unwrap(newAddressBookData);\n this.acls = {'objectEditor': 1, 'objectCreator': 1, 'objectEraser': 1};\n }\n else if (this.id) {\n this.$acl = new AddressBook.$$Acl('Contacts/' + this.id);\n }\n }\n else {\n // The promise will be unwrapped first\n this.$unwrap(futureAddressBookData);\n }\n }\n\n /**\n * @memberof AddressBook\n * @desc The factory we'll use to register with Angular\n * @returns the AddressBook constructor\n */\n AddressBook.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgAddressBook_PRELOAD', 'Resource', 'Card', 'Acl', 'Preferences', function($q, $timeout, $log, Settings, AddressBook_PRELOAD, Resource, Card, Acl, Preferences) {\n angular.extend(AddressBook, {\n $q: $q,\n $timeout: $timeout,\n $log: $log,\n PRELOAD: AddressBook_PRELOAD,\n $$resource: new Resource(Settings.activeUser('folderURL') + 'Contacts', Settings.activeUser()),\n $Card: Card,\n $$Acl: Acl,\n $Preferences: Preferences,\n $query: {search: 'name_or_address', value: '', sort: 'c_cn', asc: 1},\n activeUser: Settings.activeUser(),\n selectedFolder: null,\n $refreshTimeout: null\n });\n // Initialize sort parameters from user's settings\n Preferences.ready().then(function() {\n if (Preferences.settings.Contact.SortingState) {\n AddressBook.$query.sort = Preferences.settings.Contact.SortingState[0];\n AddressBook.$query.asc = parseInt(Preferences.settings.Contact.SortingState[1]);\n }\n });\n return AddressBook; // return constructor\n }];\n\n /**\n * @module SOGo.ContactsUI\n * @desc Factory registration of AddressBook in Angular module.\n */\n try {\n angular.module('SOGo.ContactsUI');\n }\n catch(e) {\n angular.module('SOGo.ContactsUI', ['SOGo.Common', 'SOGo.PreferencesUI']);\n }\n angular.module('SOGo.ContactsUI')\n .constant('sgAddressBook_PRELOAD', {\n LOOKAHEAD: 50,\n SIZE: 100\n })\n .factory('AddressBook', AddressBook.$factory);\n\n /**\n * @memberof AddressBook\n * @desc Search for cards among all addressbooks matching some criterias.\n * @param {string} search - the search string to match\n * @param {object} [options] - additional options to the query (excludeGroups and excludeLists)\n * @param {object[]} excludedCards - a list of Card objects that must be excluded from the results\n * @returns a collection of Cards instances\n */\n AddressBook.$filterAll = function(search, options, excludedCards) {\n var params = { search: search };\n\n if (!search) {\n // No query specified\n AddressBook.$cards = [];\n return AddressBook.$q.when(AddressBook.$cards);\n }\n if (angular.isUndefined(AddressBook.$cards)) {\n // First session query\n AddressBook.$cards = [];\n }\n\n angular.extend(params, options);\n\n return AddressBook.$$resource.fetch(null, 'allContactSearch', params).then(function(response) {\n var results, card, index,\n compareIds = function(data) {\n return this.id == data.id;\n };\n if (excludedCards) {\n // Remove excluded cards from results\n results = _.filter(response.contacts, function(data) {\n return _.isUndefined(_.find(excludedCards, _.bind(compareIds, data)));\n });\n }\n else {\n results = response.contacts;\n }\n // Remove cards that no longer match the search query\n for (index = AddressBook.$cards.length - 1; index >= 0; index--) {\n card = AddressBook.$cards[index];\n if (_.isUndefined(_.find(results, _.bind(compareIds, card)))) {\n AddressBook.$cards.splice(index, 1);\n }\n }\n // Add new cards matching the search query\n _.forEach(results, function(data, index) {\n if (_.isUndefined(_.find(AddressBook.$cards, _.bind(compareIds, data)))) {\n var card = new AddressBook.$Card(_.mapKeys(data, function(value, key) {\n return key.toLowerCase();\n }), search);\n AddressBook.$cards.splice(index, 0, card);\n }\n });\n AddressBook.$log.debug(AddressBook.$cards);\n return AddressBook.$cards;\n });\n };\n\n /**\n * @memberof AddressBook\n * @desc Add a new addressbook to the static list of addressbooks\n * @param {AddressBook} addressbook - an Addressbook object instance\n */\n AddressBook.$add = function(addressbook) {\n // Insert new addressbook at proper index\n var list, sibling, i;\n\n list = addressbook.isSubscription? this.$subscriptions : this.$addressbooks;\n sibling = _.find(list, function(o) {\n return (addressbook.id == 'personal' ||\n (o.id != 'personal' &&\n o.name.localeCompare(addressbook.name) === 1));\n });\n i = sibling ? _.indexOf(_.map(list, 'id'), sibling.id) : 1;\n list.splice(i, 0, addressbook);\n };\n\n /**\n * @memberof AddressBook\n * @desc Set or get the list of addressbooks. Will instantiate a new AddressBook object for each item.\n * @param {array} [data] - the metadata of the addressbooks\n * @returns the list of addressbooks\n */\n AddressBook.$findAll = function(data) {\n var _this = this;\n if (data) {\n this.$addressbooks = [];\n this.$subscriptions = [];\n this.$remotes = [];\n // Instanciate AddressBook objects\n angular.forEach(data, function(o, i) {\n var addressbook = new AddressBook(o);\n if (addressbook.isRemote)\n _this.$remotes.push(addressbook);\n else if (addressbook.isSubscription)\n _this.$subscriptions.push(addressbook);\n else\n _this.$addressbooks.push(addressbook);\n });\n }\n return _.union(this.$addressbooks, this.$subscriptions, this.$remotes);\n };\n\n /**\n * @memberOf AddressBook\n * @desc Subscribe to another user's addressbook and add it to the list of addressbooks.\n * @param {string} uid - user id\n * @param {string} path - path of folder for specified user\n * @returns a promise of the HTTP query result\n */\n AddressBook.$subscribe = function(uid, path) {\n var _this = this;\n return AddressBook.$$resource.userResource(uid).fetch(path, 'subscribe').then(function(addressbookData) {\n var addressbook = new AddressBook(addressbookData);\n if (_.isUndefined(_.find(_this.$subscriptions, function(o) {\n return o.id == addressbookData.id;\n }))) {\n // Not already subscribed\n AddressBook.$add(addressbook);\n }\n return addressbook;\n });\n };\n\n /**\n * @memberof AddressBook\n * @desc Reload the list of known addressbooks.\n */\n AddressBook.$reloadAll = function() {\n var _this = this;\n\n return AddressBook.$$resource.fetch('addressbooksList').then(function(data) {\n _.forEach(data.addressbooks, function(addressbookData) {\n var group, addressbook;\n\n if (addressbookData.isRemote)\n group = _this.$remotes;\n else if (addressbookData.owner != AddressBook.activeUser.login)\n group = _this.$subscriptions;\n else\n group = _this.$addressbooks;\n\n addressbook = _.find(group, function(o) { return o.id == addressbookData.id; });\n if (addressbook)\n addressbook.init(addressbookData);\n });\n });\n };\n\n /**\n * @function init\n * @memberof AddressBook.prototype\n * @desc Extend instance with new data and compute additional attributes.\n * @param {object} data - attributes of addressbook\n */\n AddressBook.prototype.init = function(data, options) {\n var _this = this;\n if (!this.$$cards) {\n // Array of cards for \"dry\" searches (see $filter)\n this.$$cards = [];\n }\n this.idsMap = {};\n this.$cards = [];\n // Extend instance with all attributes of data except headers\n angular.forEach(data, function(value, key) {\n if (key != 'headers' && key != 'cards') {\n _this[key] = value;\n }\n });\n // Add 'isOwned' and 'isSubscription' attributes based on active user (TODO: add it server-side?)\n this.isOwned = AddressBook.activeUser.isSuperUser || this.owner == AddressBook.activeUser.login;\n this.isSubscription = !this.isRemote && this.owner != AddressBook.activeUser.login;\n };\n\n /**\n * @function $id\n * @memberof AddressBook.prototype\n * @desc Resolve the addressbook id.\n * @returns a promise of the addressbook id\n */\n AddressBook.prototype.$id = function() {\n if (this.id) {\n // Object already unwrapped\n return AddressBook.$q.when(this.id);\n }\n else {\n // Wait until object is unwrapped\n return this.$futureAddressBookData.then(function(addressbook) {\n return addressbook.id;\n });\n }\n };\n\n /**\n * @function getLength\n * @memberof AddressBook.prototype\n * @desc Used by md-virtual-repeat / md-on-demand\n * @returns the number of cards in the addressbook\n */\n AddressBook.prototype.getLength = function() {\n return this.$cards.length;\n };\n\n /**\n * @function getItemAtIndex\n * @memberof AddressBook.prototype\n * @desc Used by md-virtual-repeat / md-on-demand\n * @returns the card at the specified index\n */\n AddressBook.prototype.getItemAtIndex = function(index) {\n var card;\n\n if (!this.$isLoading && index >= 0 && index < this.$cards.length) {\n card = this.$cards[index];\n this.$lastVisibleIndex = Math.max(0, index - 3); // Magic number is NUM_EXTRA from virtual-repeater.js\n\n if (this.$loadCard(card))\n return card;\n }\n return null;\n };\n\n /**\n * @function $loadCard\n * @memberof AddressBook.prototype\n * @desc Check if the card is loaded and in any case, fetch more cards headers from the server.\n * @returns true if the card metadata are already fetched\n */\n AddressBook.prototype.$loadCard = function(card) {\n var cardId = card.id,\n startIndex = this.idsMap[cardId],\n endIndex,\n index,\n max = this.$cards.length,\n loaded = false,\n ids,\n futureHeadersData;\n\n if (angular.isUndefined(this.ids) && card.id) {\n loaded = true;\n }\n else if (angular.isDefined(startIndex) && startIndex < this.$cards.length) {\n // Index is valid\n if (card.$loaded != AddressBook.$Card.STATUS.NOT_LOADED) {\n // Card headers are loaded or data is coming\n loaded = true;\n }\n\n // Preload more headers if possible\n endIndex = Math.min(startIndex + AddressBook.PRELOAD.LOOKAHEAD, max - 1);\n if (this.$cards[endIndex].$loaded != AddressBook.$Card.STATUS.NOT_LOADED) {\n index = Math.max(startIndex - AddressBook.PRELOAD.LOOKAHEAD, 0);\n if (this.$cards[index].$loaded != AddressBook.$Card.STATUS.LOADED) {\n // Previous cards not loaded; preload more headers further up\n endIndex = startIndex;\n startIndex = Math.max(startIndex - AddressBook.PRELOAD.SIZE, 0);\n }\n }\n else\n // Next cards not load; preload more headers further down\n endIndex = Math.min(startIndex + AddressBook.PRELOAD.SIZE, max - 1);\n\n if (this.$cards[startIndex].$loaded == AddressBook.$Card.STATUS.NOT_LOADED ||\n this.$cards[endIndex].$loaded == AddressBook.$Card.STATUS.NOT_LOADED) {\n\n for (ids = []; startIndex < endIndex && startIndex < max; startIndex++) {\n if (this.$cards[startIndex].$loaded != AddressBook.$Card.STATUS.NOT_LOADED) {\n // Card at this index is already loaded; increase the end index\n endIndex++;\n }\n else {\n // Card at this index will be loaded\n ids.push(this.$cards[startIndex].id);\n this.$cards[startIndex].$loaded = AddressBook.$Card.STATUS.LOADING;\n }\n }\n\n AddressBook.$log.debug('Loading Ids ' + ids.join(' ') + ' (' + ids.length + ' cards)');\n if (ids.length > 0) {\n futureHeadersData = AddressBook.$$resource.post(this.id, 'headers', {ids: ids});\n this.$unwrapHeaders(futureHeadersData);\n }\n }\n }\n return loaded;\n };\n\n /**\n * @function hasSelectedMessage\n * @memberof AddressBook.prototype\n * @desc Check if a card is selected.\n * @returns true if the a card is selected\n */\n AddressBook.prototype.hasSelectedCard = function() {\n return angular.isDefined(this.selectedCard);\n };\n\n /**\n * @function isSelectedCard\n * @memberof AddressBook.prototype\n * @desc Check if the specified card is selected.\n * @param {string} CardId\n * @returns true if the specified card is selected\n */\n AddressBook.prototype.isSelectedCard = function(cardId) {\n return this.hasSelectedCard() && this.selectedCard == cardId;\n };\n\n /**\n * @function $selectedCard\n * @memberof AddressBook.prototype\n * @desc Return the currently visible card.\n * @returns a Card instance or undefined if no card is displayed\n */\n AddressBook.prototype.$selectedCard = function() {\n var _this = this;\n\n return _.find(this.$cards, function(card) { return card.id == _this.selectedCard; });\n };\n\n /**\n * @function $selectedCardIndex\n * @memberof AddressBook.prototype\n * @desc Return the index of the currently visible card.\n * @returns a number or undefined if no card is selected\n */\n AddressBook.prototype.$selectedCardIndex = function() {\n return _.indexOf(_.map(this.$cards, 'id'), this.selectedCard);\n };\n\n /**\n * @function $selectedCards\n * @memberof AddressBook.prototype\n * @desc Return the cards selected by the user.\n * @returns Card instances\n */\n AddressBook.prototype.$selectedCards = function() {\n return _.filter(this.$cards, function(card) { return card.selected; });\n };\n\n /**\n * @function $selectedCount\n * @memberof AddressBook.prototype\n * @desc Return the number of cards selected by the user.\n * @returns the number of selected cards\n */\n AddressBook.prototype.$selectedCount = function() {\n var count;\n\n count = 0;\n if (this.$cards) {\n count = (_.filter(this.$cards, function(card) { return card.selected; })).length;\n }\n return count;\n };\n\n /**\n * @function $startRefreshTimeout\n * @memberof AddressBook.prototype\n * @desc Starts the refresh timeout for the current selected address book\n */\n AddressBook.prototype.$startRefreshTimeout = function() {\n var _this = this;\n\n if (AddressBook.$refreshTimeout)\n AddressBook.$timeout.cancel(AddressBook.$refreshTimeout);\n\n AddressBook.$Preferences.ready().then(function() {\n // Restart the refresh timer, if needed\n var refreshViewCheck = AddressBook.$Preferences.defaults.SOGoRefreshViewCheck;\n if (refreshViewCheck && refreshViewCheck != 'manually') {\n var f = angular.bind(_this, AddressBook.prototype.$reload);\n AddressBook.$refreshTimeout = AddressBook.$timeout(f, refreshViewCheck.timeInterval()*1000);\n }\n });\n };\n\n /**\n * @function $reload\n * @memberof AddressBook.prototype\n * @desc Reload list of cards\n * @returns a promise of the Cards instances\n */\n AddressBook.prototype.$reload = function() {\n var _this = this;\n\n this.$startRefreshTimeout();\n return this.$filter();\n };\n\n /**\n * @function $filter\n * @memberof AddressBook.prototype\n * @desc Search for cards matching some criterias\n * @param {string} search - the search string to match\n * @param {object} [options] - additional options to the query (dry, excludeList)\n * @returns a collection of Cards instances\n */\n AddressBook.prototype.$filter = function(search, options, excludedCards) {\n var _this = this, query,\n dry = options && options.dry;\n\n if (dry) {\n // Don't keep a copy of the query in dry mode\n query = angular.copy(AddressBook.$query);\n }\n else {\n this.$isLoading = true;\n query = AddressBook.$query;\n if (!this.isRemote) query.partial = 1;\n }\n\n return AddressBook.$Preferences.ready().then(function() {\n if (options) {\n angular.extend(query, options);\n if (dry) {\n if (!search) {\n // No query specified\n _this.$$cards = [];\n return AddressBook.$q.when(_this.$$cards);\n }\n }\n }\n\n if (angular.isDefined(search))\n query.value = search;\n\n return _this.$id().then(function(addressbookId) {\n var futureData = AddressBook.$$resource.fetch(addressbookId, 'view', query);\n\n if (dry) {\n return futureData.then(function(response) {\n var results, headers, card, index, fields, idFieldIndex,\n cards = _this.$$cards,\n compareIds = function(card) {\n return this == card.id;\n };\n\n if (response.headers) {\n // First entry of 'headers' are keys\n fields = _.invokeMap(response.headers[0], 'toLowerCase');\n idFieldIndex = fields.indexOf('id');\n response.headers.splice(0, 1);\n }\n\n if (excludedCards)\n // Remove excluded cards from results\n results = _.filter(response.ids, function(id) {\n return _.isUndefined(_.find(excludedCards, _.bind(compareIds, id)));\n });\n else\n results = response.ids;\n\n // Remove cards that no longer match the search query\n for (index = cards.length - 1; index >= 0; index--) {\n card = cards[index];\n if (_.isUndefined(_.find(results, _.bind(compareIds, card.id)))) {\n cards.splice(index, 1);\n }\n }\n\n // Add new cards matching the search query\n _.forEach(results, function(cardId, index) {\n if (_.isUndefined(_.find(cards, _.bind(compareIds, cardId)))) {\n var data = { pid: addressbookId, id: cardId };\n var card = new AddressBook.$Card(data, search);\n cards.splice(index, 0, card);\n }\n });\n\n // Respect the order of the results\n _.forEach(results, function(cardId, index) {\n var oldIndex, removedCards;\n if (cards[index].id != cardId) {\n oldIndex = _.findIndex(cards, _.bind(compareIds, cardId));\n removedCards = cards.splice(oldIndex, 1);\n cards.splice(index, 0, removedCards[0]);\n }\n });\n\n // Extend Card objects with received headers\n _.forEach(response.headers, function(data) {\n var card, index = _.findIndex(cards, _.bind(compareIds, data[idFieldIndex]));\n if (index > -1) {\n card = _.zipObject(fields, data);\n cards[index].init(card, search);\n }\n });\n\n return cards;\n });\n }\n else {\n // Unwrap promise and instantiate or extend Cards objets\n return _this.$unwrap(futureData);\n }\n });\n });\n };\n\n /**\n * @function $rename\n * @memberof AddressBook.prototype\n * @desc Rename the addressbook and keep the list sorted\n * @param {string} name - the new name\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.$rename = function(name) {\n var i, list;\n\n list = this.isSubscription? AddressBook.$subscriptions : AddressBook.$addressbooks;\n i = _.indexOf(_.map(list, 'id'), this.id);\n this.name = name;\n list.splice(i, 1);\n AddressBook.$add(this);\n\n return this.$save();\n };\n\n /**\n * @function $delete\n * @memberof AddressBook.prototype\n * @desc Delete the addressbook from the server and the static list of addressbooks.\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.$delete = function() {\n var _this = this,\n d = AddressBook.$q.defer(),\n list,\n promise;\n\n if (this.isSubscription) {\n promise = AddressBook.$$resource.fetch(this.id, 'unsubscribe');\n list = AddressBook.$subscriptions;\n }\n else {\n promise = AddressBook.$$resource.remove(this.id);\n list = AddressBook.$addressbooks;\n }\n\n promise.then(function() {\n var i = _.indexOf(_.map(list, 'id'), _this.id);\n list.splice(i, 1);\n d.resolve();\n }, d.reject);\n return d.promise;\n };\n\n /**\n * @function $_deleteCards\n * @memberof AddressBook.prototype\n * @desc Delete multiple cards from AddressBook object.\n * @param {string[]} ids - the cards ids\n */\n AddressBook.prototype.$_deleteCards = function(ids) {\n var _this = this;\n\n // Remove cards from $cards and idsMap\n _.forEachRight(this.$cards, function(card, index) {\n var selectedIndex = _.findIndex(ids, function(id) {\n return card.id == id;\n });\n if (selectedIndex > -1) {\n ids.splice(selectedIndex, 1);\n delete _this.idsMap[card.id];\n if (_this.isSelectedCard(card.id))\n delete _this.selectedCard;\n _this.$cards.splice(index, 1);\n }\n else {\n _this.idsMap[card.id] -= ids.length;\n }\n });\n };\n\n /**\n * @function $deleteCards\n * @memberof AddressBook.prototype\n * @desc Delete multiple cards from addressbook.\n * @return a promise of the HTTP operation\n */\n AddressBook.prototype.$deleteCards = function(cards) {\n var _this = this,\n ids = _.map(cards, 'id');\n \n return AddressBook.$$resource.post(this.id, 'batchDelete', {uids: ids}).then(function() {\n _this.$_deleteCards(ids);\n });\n };\n\n /**\n * @function $copyCards\n * @memberof AddressBook.prototype\n * @desc Copy multiple cards from addressbook to an other one.\n * @return a promise of the HTTP operation\n */\n AddressBook.prototype.$copyCards = function(cards, folder) {\n var uids = _.map(cards, 'id');\n return AddressBook.$$resource.post(this.id, 'copy', {uids: uids, folder: folder});\n };\n\n /**\n * @function $moveCards\n * @memberof AddressBook.prototype\n * @desc Move multiple cards from the current addressbook to a target one\n * @param {object[]} cards - instances of Card object\n * @param {string} folder - the destination folder id\n * @return a promise of the HTTP operation\n */\n AddressBook.prototype.$moveCards = function(cards, folder) {\n var _this = this, uids;\n\n uids = _.map(cards, 'id');\n return AddressBook.$$resource.post(this.id, 'move', {uids: uids, folder: folder})\n .then(function() {\n return _this.$_deleteCards(uids);\n });\n };\n\n /**\n * @function $save\n * @memberof AddressBook.prototype\n * @desc Save the addressbook to the server. This currently can only affect the name of the addressbook.\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.$save = function() {\n return AddressBook.$$resource.save(this.id, this.$omit()).then(function(data) {\n return data;\n });\n };\n\n /**\n * @function $exportCards\n * @memberof AddressBook.prototype\n * @desc Export the selected/all cards\n * @returns a promise of the HTTP operation\n */\n AddressBook.prototype.exportCards = function(selectedOnly) {\n var selectedUIDs;\n\n if (selectedOnly) {\n var selectedCards = _.filter(this.$cards, function(card) { return card.selected; });\n selectedUIDs = _.map(selectedCards, 'id');\n }\n\n return AddressBook.$$resource.download(this.id, 'export', (angular.isDefined(selectedUIDs) ? {uids: selectedUIDs} : null), {type: 'application/octet-stream'});\n };\n\n /**\n * @function $unwrap\n * @memberof AddressBook.prototype\n * @desc Unwrap a promise and instanciate new Card objects using received data.\n * @param {promise} futureAddressBookData - a promise of the AddressBook's data\n */\n AddressBook.prototype.$unwrap = function(futureAddressBookData) {\n var _this = this;\n\n this.$isLoading = true;\n\n // Expose and resolve the promise\n this.$futureAddressBookData = futureAddressBookData.then(function(response) {\n return AddressBook.$timeout(function() {\n var headers;\n\n if (!response.ids || _this.$topIndex > response.ids.length - 1)\n _this.$topIndex = 0;\n\n // Extend AddressBook instance from data of addressbooks list.\n // Will inherit attributes such as isEditable and isRemote.\n angular.forEach(AddressBook.$findAll(), function(o, i) {\n if (o.id == response.id) {\n angular.extend(_this, o);\n }\n });\n\n // Extend AddressBook instance with received data\n _this.init(response);\n\n if (_this.ids) {\n AddressBook.$log.debug('unwrapping ' + _this.ids.length + ' cards');\n\n // Instanciate Card objects\n _.reduce(_this.ids, function(cards, card, i) {\n var data = { pid: _this.id, id: card };\n\n // Build map of ID <=> index\n _this.idsMap[data.id] = i;\n\n cards.push(new AddressBook.$Card(data));\n\n return cards;\n }, _this.$cards);\n }\n\n if (response.headers) {\n // First entry of 'headers' are keys\n headers = _.invokeMap(response.headers[0], 'toLowerCase');\n response.headers.splice(0, 1);\n\n if (_this.ids) {\n // Extend Card objects with received headers\n _.forEach(response.headers, function(data) {\n var o = _.zipObject(headers, data),\n i = _this.idsMap[o.id];\n _this.$cards[i].init(o);\n });\n }\n else {\n // Instanciate Card objects\n _this.$cards = [];\n angular.forEach(response.headers, function(data) {\n var o = _.zipObject(headers, data);\n angular.extend(o, { pid: _this.id });\n _this.$cards.push(new AddressBook.$Card(o));\n });\n }\n }\n\n // Instanciate Acl object\n _this.$acl = new AddressBook.$$Acl('Contacts/' + _this.id);\n\n _this.$startRefreshTimeout();\n\n _this.$isLoading = false;\n\n AddressBook.$log.debug('addressbook ' + _this.id + ' ready');\n\n return _this;\n });\n }, function(data) {\n _this.isError = true;\n if (angular.isObject(data)) {\n AddressBook.$timeout(function() {\n angular.extend(_this, data);\n });\n }\n });\n };\n\n /**\n * @function $unwrapHeaders\n * @memberof AddressBook.prototype\n * @desc Unwrap a promise and extend matching Card objects with received data.\n * @param {promise} futureHeadersData - a promise of the metadata of some cards\n */\n AddressBook.prototype.$unwrapHeaders = function(futureHeadersData) {\n var _this = this;\n\n futureHeadersData.then(function(data) {\n AddressBook.$timeout(function() {\n var headers, j;\n if (data.length > 0) {\n // First entry of 'headers' are keys\n headers = _.invokeMap(data[0], 'toLowerCase');\n data.splice(0, 1);\n _.forEach(data, function(cardHeaders) {\n cardHeaders = _.zipObject(headers, cardHeaders);\n j = _this.idsMap[cardHeaders.id];\n if (angular.isDefined(j)) {\n _this.$cards[j].init(cardHeaders);\n }\n });\n }\n });\n });\n };\n\n /**\n * @function $omit\n * @memberof AddressBook.prototype\n * @desc Return a sanitized object used to send to the server.\n * @return an object literal copy of the Addressbook instance\n */\n AddressBook.prototype.$omit = function() {\n var addressbook = {};\n angular.forEach(this, function(value, key) {\n if (key != 'constructor' &&\n key != 'acls' &&\n key != 'ids' &&\n key != 'idsMap' &&\n key != 'urls' &&\n key[0] != '$') {\n addressbook[key] = value;\n }\n });\n return addressbook;\n };\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n\n(function() {\n 'use strict';\n\n /**\n * @name Card\n * @constructor\n * @param {object} futureCardData\n * @param {string} [partial]\n */\n function Card(futureCardData, partial) {\n\n // Data is immediately available\n if (typeof futureCardData.then !== 'function') {\n this.init(futureCardData, partial);\n if (this.pid && !this.id) {\n // Prepare for the creation of a new card;\n // Get UID from the server.\n var newCardData = Card.$$resource.newguid(this.pid);\n this.$unwrap(newCardData);\n this.isNew = true;\n }\n }\n else {\n // The promise will be unwrapped first\n this.$unwrap(futureCardData);\n }\n }\n\n Card.$TEL_TYPES = ['work', 'home', 'cell', 'fax', 'pager'];\n Card.$EMAIL_TYPES = ['work', 'home', 'pref'];\n Card.$URL_TYPES = ['work', 'home', 'pref'];\n Card.$ADDRESS_TYPES = ['work', 'home'];\n\n /**\n * @memberof Card\n * @desc The factory we'll use to register with Angular.\n * @returns the Card constructor\n */\n Card.$factory = ['$timeout', 'sgSettings', 'sgCard_STATUS', 'Resource', 'Preferences', function($timeout, Settings, Card_STATUS, Resource, Preferences) {\n angular.extend(Card, {\n STATUS: Card_STATUS,\n $$resource: new Resource(Settings.activeUser('folderURL') + 'Contacts', Settings.activeUser()),\n $timeout: $timeout,\n $Preferences: Preferences\n });\n // Initialize categories from user's defaults\n Preferences.ready().then(function() {\n if (Preferences.defaults.SOGoContactsCategories) {\n Card.$categories = Preferences.defaults.SOGoContactsCategories;\n }\n if (Preferences.defaults.SOGoAlternateAvatar)\n Card.$alternateAvatar = Preferences.defaults.SOGoAlternateAvatar;\n });\n\n return Card; // return constructor\n }];\n\n /**\n * @module SOGo.ContactsUI\n * @desc Factory registration of Card in Angular module.\n */\n try {\n angular.module('SOGo.ContactsUI');\n }\n catch(e) {\n angular.module('SOGo.ContactsUI', ['SOGo.Common', 'SOGo.PreferencesUI']);\n }\n angular.module('SOGo.ContactsUI')\n .constant('sgCard_STATUS', {\n NOT_LOADED: 0,\n DELAYED_LOADING: 1,\n LOADING: 2,\n LOADED: 3,\n DELAYED_MS: 300\n })\n .factory('Card', Card.$factory);\n\n /**\n * @memberof Card\n * @desc Fetch a card from a specific addressbook.\n * @param {string} addressbookId - the addressbook ID\n * @param {string} cardId - the card ID\n * @see {@link AddressBook.$getCard}\n */\n Card.$find = function(addressbookId, cardId) {\n var futureCardData = this.$$resource.fetch([addressbookId, cardId].join('/'), 'view');\n\n if (cardId) return new Card(futureCardData); // a single card\n\n return Card.$unwrapCollection(futureCardData); // a collection of cards\n };\n\n /**\n * @function filterCategories\n * @memberof Card.prototype\n * @desc Search for categories matching some criterias\n * @param {string} search - the search string to match\n * @returns a collection of strings\n */\n Card.filterCategories = function(query) {\n var re = new RegExp(query, 'i');\n return _.map(_.filter(Card.$categories, function(category) {\n return category.search(re) != -1;\n }), function(category) {\n return { value: category };\n });\n };\n\n /**\n * @memberof Card\n * @desc Unwrap to a collection of Card instances.\n * @param {object} futureCardData\n */\n Card.$unwrapCollection = function(futureCardData) {\n var collection = {};\n\n collection.$futureCardData = futureCardData;\n\n futureCardData.then(function(cards) {\n Card.$timeout(function() {\n angular.forEach(cards, function(data, index) {\n collection[data.id] = new Card(data);\n });\n });\n });\n\n return collection;\n };\n\n /**\n * @function init\n * @memberof Card.prototype\n * @desc Extend instance with required attributes and new data.\n * @param {object} data - attributes of card\n */\n Card.prototype.init = function(data, partial) {\n var _this = this;\n\n this.refs = [];\n this.categories = [];\n this.c_screenname = null;\n angular.extend(this, data);\n if (!this.$$fullname)\n this.$$fullname = this.$fullname();\n if (!this.$$email)\n this.$$email = this.$preferredEmail(partial);\n if (!this.$$image)\n this.$$image = this.image;\n if (!this.$$image)\n Card.$Preferences.avatar(this.$$email, 32, {no_404: true}).then(function(url) {\n _this.$$image = url;\n });\n if (this.isgroup)\n this.c_component = 'vlist';\n this.$avatarIcon = this.$isList()? 'group' : 'person';\n if (data.notes && data.notes.length)\n this.notes = _.map(data.notes, function(note) { return { 'value': note }; });\n else if (!this.notes || !this.notes.length)\n this.notes = [ { value: '' } ];\n // Instanciate Card objects for list members\n angular.forEach(this.refs, function(o, i) {\n if (o.email) o.emails = [{value: o.email}];\n o.id = o.reference;\n _this.refs[i] = new Card(o);\n });\n // Instanciate date object of birthday\n if (this.birthday) {\n Card.$Preferences.ready().then(function() {\n var dlp = Card.$Preferences.$mdDateLocaleProvider;\n _this.birthday = _this.birthday.parseDate(dlp, '%Y-%m-%d');\n _this.$birthday = dlp.formatDate(_this.birthday);\n });\n }\n\n this.$loaded = angular.isDefined(this.c_name)? Card.STATUS.LOADED : Card.STATUS.NOT_LOADED;\n\n // An empty attribute to trick md-autocomplete when adding attendees from the appointment editor\n this.empty = ' ';\n };\n\n /**\n * @function $id\n * @memberof Card.prototype\n * @desc Return the card ID.\n * @returns the card ID\n */\n Card.prototype.$id = function() {\n return this.$futureCardData.then(function(data) {\n return data.id;\n });\n };\n\n /**\n * @function $isLoading\n * @memberof Card.prototype\n * @returns true if the Card definition is still being retrieved from server after a specific delay\n * @see sgCard_STATUS\n */\n Card.prototype.$isLoading = function() {\n return this.$loaded == Card.STATUS.LOADING;\n };\n\n /**\n * @function $reload\n * @memberof Message.prototype\n * @desc Fetch the viewable message body along with other metadata such as the list of attachments.\n * @returns a promise of the HTTP operation\n */\n Card.prototype.$reload = function() {\n var futureCardData;\n\n if (this.$futureCardData)\n return this;\n\n futureCardData = Card.$$resource.fetch([this.pid, this.id].join('/'), 'view');\n\n return this.$unwrap(futureCardData);\n };\n\n /**\n * @function $save\n * @memberof Card.prototype\n * @desc Save the card to the server.\n */\n Card.prototype.$save = function() {\n var _this = this,\n action = 'saveAsContact';\n\n if (this.c_component == 'vlist') {\n action = 'saveAsList';\n _.forEach(this.refs, function(ref) {\n ref.reference = ref.id;\n });\n }\n\n return Card.$$resource.save([this.pid, this.id || '_new_'].join('/'),\n this.$omit(),\n { action: action })\n .then(function(data) {\n // Format birthdate\n if (_this.birthday)\n _this.$birthday = Card.$Preferences.$mdDateLocaleProvider.formatDate(_this.birthday);\n // Make a copy of the data for an eventual reset\n _this.$shadowData = _this.$omit(true);\n return data;\n });\n };\n\n Card.prototype.$delete = function(attribute, index) {\n if (attribute) {\n if (index > -1 && this[attribute].length > index) {\n this[attribute].splice(index, 1);\n }\n else\n delete this[attribute];\n }\n else {\n // No arguments -- delete card\n return Card.$$resource.remove([this.pid, this.id].join('/'));\n }\n };\n\n /**\n * @function export\n * @memberof Card.prototype\n * @desc Download the current card\n * @returns a promise of the HTTP operation\n */\n Card.prototype.export = function() {\n var selectedIDs;\n\n selectedIDs = [ this.id ];\n\n return Card.$$resource.download(this.pid, 'export', {uids: selectedIDs}, {type: 'application/octet-stream'});\n };\n\n Card.prototype.$fullname = function(options) {\n var fn = this.c_cn || '', html = options && options.html, email, names;\n if (fn.length === 0) {\n names = [];\n if (this.c_givenname && this.c_givenname.length > 0)\n names.push(this.c_givenname);\n if (this.nickname && this.nickname.length > 0)\n names.push((html?'':'') + this.nickname + (html?'':''));\n if (this.c_sn && this.c_sn.length > 0)\n names.push(this.c_sn);\n if (names.length > 0)\n fn = names.join(' ');\n else if (this.c_org && this.c_org.length > 0) {\n fn = this.c_org;\n }\n else if (this.emails && this.emails.length > 0) {\n email = _.find(this.emails, function(i) { return i.value !== ''; });\n if (email)\n fn = email.value;\n }\n }\n\n return fn;\n };\n\n Card.prototype.$description = function() {\n var description = [];\n if (this.title) description.push(this.title);\n if (this.role) description.push(this.role);\n if (this.orgUnits && this.orgUnits.length > 0)\n _.forEach(this.orgUnits, function(unit) {\n if (unit.value !== '')\n description.push(unit.value);\n });\n if (this.c_org) description.push(this.c_org);\n if (this.description) description.push(this.description);\n\n return description.join(', ');\n };\n\n /**\n * @function $preferredEmail\n * @memberof Card.prototype\n * @desc Get the preferred email address.\n * @param {string} [partial] - a partial string that the email must match\n * @returns the first email address of type \"pref\" or the first address if none found\n */\n Card.prototype.$preferredEmail = function(partial) {\n var email, re;\n if (partial) {\n re = new RegExp(partial, 'i');\n email = _.find(this.emails, function(o) {\n return re.test(o.value);\n });\n }\n if (email) {\n email = email.value;\n }\n else {\n email = _.find(this.emails, function(o) {\n return o.type == 'pref';\n });\n if (email) {\n email = email.value;\n }\n else if (this.emails && this.emails.length) {\n email = this.emails[0].value;\n }\n else if (this.c_mail && this.c_mail.length) {\n email = this.c_mail[0];\n }\n else {\n email = '';\n }\n }\n\n return email;\n };\n\n /**\n * @function $shortFormat\n * @memberof Card.prototype\n * @param {string} [partial] - a partial string that the email must match\n * @returns the fullname along with a matching email address in parentheses\n */\n Card.prototype.$shortFormat = function(partial) {\n var fullname = [this.$$fullname],\n email = this.$preferredEmail(partial);\n if (email && email != this.$$fullname)\n fullname.push(' <' + email + '>');\n return fullname.join(' ');\n };\n\n Card.prototype.$isCard = function() {\n return this.c_component == 'vcard';\n };\n\n Card.prototype.$isList = function(options) {\n // isGroup attribute means it's a group of a LDAP source (not expandable on the client-side)\n var condition = (!options || !options.expandable || options.expandable && !this.isgroup);\n return this.c_component == 'vlist' && condition;\n };\n\n Card.prototype.$addOrgUnit = function(orgUnit) {\n if (angular.isUndefined(this.orgUnits)) {\n this.orgUnits = [{value: orgUnit}];\n }\n else {\n for (var i = 0; i < this.orgUnits.length; i++) {\n if (this.orgUnits[i].value == orgUnit) {\n break;\n }\n }\n if (i == this.orgUnits.length)\n this.orgUnits.push({value: orgUnit});\n }\n return this.orgUnits.length - 1;\n };\n\n // Card.prototype.$addCategory = function(category) {\n // if (category) {\n // if (angular.isUndefined(this.categories)) {\n // this.categories = [{value: category}];\n // }\n // else {\n // for (var i = 0; i < this.categories.length; i++) {\n // if (this.categories[i].value == category) {\n // break;\n // }\n // }\n // if (i == this.categories.length)\n // this.categories.push({value: category});\n // }\n // }\n // };\n\n Card.prototype.$addEmail = function(type) {\n if (angular.isUndefined(this.emails)) {\n this.emails = [{type: type, value: ''}];\n }\n else if (_.isUndefined(_.find(this.emails, function(i) { return i.value === ''; }))) {\n this.emails.push({type: type, value: ''});\n }\n return this.emails.length - 1;\n };\n\n Card.prototype.$addScreenName = function(screenName) {\n this.c_screenname = screenName;\n };\n\n Card.prototype.$addPhone = function(type) {\n if (angular.isUndefined(this.phones)) {\n this.phones = [{type: type, value: ''}];\n }\n else if (_.isUndefined(_.find(this.phones, function(i) { return i.value === ''; }))) {\n this.phones.push({type: type, value: ''});\n }\n return this.phones.length - 1;\n };\n\n Card.prototype.$addUrl = function(type, url) {\n if (angular.isUndefined(this.urls)) {\n this.urls = [{type: type, value: url}];\n }\n else if (_.isUndefined(_.find(this.urls, function(i) { return i.value == url; }))) {\n this.urls.push({type: type, value: url});\n }\n return this.urls.length - 1;\n };\n\n Card.prototype.$addAddress = function(type, postoffice, street, street2, locality, region, country, postalcode) {\n if (angular.isUndefined(this.addresses)) {\n this.addresses = [{type: type, postoffice: postoffice, street: street, street2: street2, locality: locality, region: region, country: country, postalcode: postalcode}];\n }\n else if (!_.find(this.addresses, function(i) {\n return i.street == street &&\n i.street2 == street2 &&\n i.locality == locality &&\n i.country == country &&\n i.postalcode == postalcode;\n })) {\n this.addresses.push({type: type, postoffice: postoffice, street: street, street2: street2, locality: locality, region: region, country: country, postalcode: postalcode});\n }\n return this.addresses.length - 1;\n };\n\n Card.prototype.$addMember = function(email) {\n var card = new Card({email: email, emails: [{value: email}]}),\n i;\n if (angular.isUndefined(this.refs)) {\n this.refs = [card];\n }\n else if (email.length === 0) {\n this.refs.push(card);\n }\n else {\n for (i = 0; i < this.refs.length; i++) {\n if (this.refs[i].email == email) {\n break;\n }\n }\n if (i == this.refs.length)\n this.refs.push(card);\n }\n return this.refs.length - 1;\n };\n\n /**\n * @function explode\n * @memberof Card.prototype\n * @desc Create a new Card associated to each email address of this card.\n * @return an array of Card instances\n */\n Card.prototype.explode = function() {\n var _this = this, cards = [], data;\n\n if (this.emails.length > 1) {\n data = this.$omit();\n _.forEach(this.emails, function(email) {\n var card = new Card(angular.extend({}, data, {emails: [email]}));\n cards.push(card);\n });\n return cards;\n }\n else\n return [this];\n };\n\n /**\n * @function $reset\n * @memberof Card.prototype\n * @desc Reset the original state the card's data.\n */\n Card.prototype.$reset = function() {\n var _this = this;\n angular.forEach(this, function(value, key) {\n if (key != 'constructor' && key[0] != '$') {\n delete _this[key];\n }\n });\n this.init(this.$shadowData);\n this.$shadowData = this.$omit(true);\n };\n\n /**\n * @function $updateMember\n * @memberof Card.prototype\n * @desc Update an existing list member from a Card instance.\n * A list member has the following attribtues:\n * - email\n * - reference\n * - fn\n * @param {number} index\n * @param {string} email\n * @param {Card} card\n */\n // Card.prototype.$updateMember = function(index, email, card) {\n // var ref = {\n // email: email,\n // emails: [{value: email}],\n // reference: card.c_name,\n // c_cn: card.$fullname()\n // };\n // this.refs[index] = new Card(ref);\n // };\n\n /**\n * @function $unwrap\n * @memberof Card.prototype\n * @desc Unwrap a promise and make a copy of the resolved data.\n * @param {object} futureCardData - a promise of the Card's data\n */\n Card.prototype.$unwrap = function(futureCardData) {\n var _this = this;\n\n // Card is not loaded yet\n this.$loaded = Card.STATUS.DELAYED_LOADING;\n Card.$timeout(function() {\n if (_this.$loaded != Card.STATUS.LOADED)\n _this.$loaded = Card.STATUS.LOADING;\n }, Card.STATUS.DELAYED_MS);\n\n // Expose the promise\n this.$futureCardData = futureCardData.then(function(data) {\n _this.init(data);\n // Mark card as loaded\n _this.$loaded = Card.STATUS.LOADED;\n // Make a copy of the data for an eventual reset\n _this.$shadowData = _this.$omit(true);\n\n return _this;\n });\n\n return this.$futureCardData;\n };\n\n /**\n * @function $omit\n * @memberof Card.prototype\n * @desc Return a sanitized object used to send to the server.\n * @param {boolean} [deep] - make a deep copy if true\n * @return an object literal copy of the Card instance\n */\n Card.prototype.$omit = function(deep) {\n var card = {};\n angular.forEach(this, function(value, key) {\n if (key == 'refs') {\n card.refs = _.map(value, function(o) {\n return o.$omit(deep);\n });\n }\n else if (key != 'constructor' && key[0] != '$') {\n if (deep)\n card[key] = angular.copy(value);\n else\n card[key] = value;\n }\n });\n\n // We convert back our birthday object\n if (!deep) {\n if (card.birthday)\n card.birthday = card.birthday.format(Card.$Preferences.$mdDateLocaleProvider, '%Y-%m-%d');\n else\n card.birthday = '';\n }\n\n // We flatten the notes to an array of strings\n if (this.notes)\n card.notes = _.map(this.notes, 'value');\n\n return card;\n };\n\n Card.prototype.toString = function() {\n var desc = this.id + ' ' + this.$$fullname;\n\n if (this.$$email)\n desc += ' <' + this.$$email + '>';\n\n return '[' + desc + ']';\n };\n})();\n"]} \ No newline at end of file diff --git a/UI/WebServerResources/js/Preferences.js b/UI/WebServerResources/js/Preferences.js index 4f5ab31b5..3c9cb23a8 100644 --- a/UI/WebServerResources/js/Preferences.js +++ b/UI/WebServerResources/js/Preferences.js @@ -1,2 +1,2 @@ -!function(){"use strict";function a(a,c){a.state("preferences",{abstract:!0,views:{preferences:{templateUrl:"preferences.html",controller:"PreferencesController",controllerAs:"app"}},resolve:{statePreferences:b}}).state("preferences.general",{url:"/general",views:{module:{templateUrl:"generalPreferences.html"}}}).state("preferences.calendars",{url:"/calendars",views:{module:{templateUrl:"calendarsPreferences.html"}}}).state("preferences.addressbooks",{url:"/addressbooks",views:{module:{templateUrl:"addressbooksPreferences.html"}}}).state("preferences.mailer",{url:"/mailer",views:{module:{templateUrl:"mailerPreferences.html"}}}),c.otherwise("/general")}function b(a){return a}function c(a){a.$on("$routeChangeError",function(a,b,c,d){console.error(a,b,c,d)})}angular.module("SOGo.PreferencesUI",["ui.router","ck","SOGo.Common","SOGo.MailerUI","SOGo.ContactsUI","SOGo.Authentication","as.sortable"]).config(a).run(c),a.$inject=["$stateProvider","$urlRouterProvider"],b.$inject=["Preferences"],c.$inject=["$rootScope"]}(),function(){"use strict";function a(a,b,c,d,e){function f(){return!(d>0)&&!e}function g(){a.cancel()}function h(){a.hide()}var i=this;i.defaultPort=143,i.defaults=b,i.account=c,i.accountId=d,i.customFromIsReadonly=f,i.cancel=g,i.save=h,i.account.encryption?"ssl"==i.account.encryption&&(i.defaultPort=993):i.account.encryption="none"}a.$inject=["$mdDialog","defaults","account","accountId","mailCustomFromEnabled"],angular.module("SOGo.PreferencesUI").controller("AccountDialogController",a)}(),function(){"use strict";function a(a,b,c,d,e,f){function g(){c.cancel()}function h(){return o.filter.rules&&o.filter.rules.length>0&&o.filter.actions&&o.filter.actions.length>0}function i(a){c.hide()}function j(a){o.filter.rules||(o.filter.rules=[]),o.filter.rules.push({field:"subject",operator:"contains"})}function k(a){o.filter.rules.splice(a,1)}function m(a){o.filter.actions||(o.filter.actions=[]),o.filter.actions.push({method:"discard"})}function n(a){o.filter.actions.splice(a,1)}var o=this,p=b.sieveCapabilities,q=b.forwardEnabled,r=(b.vacationEnabled,"UTF-8"==b.sieveFolderEncoding?"name":"path");o.filter=d,o.mailboxes=e,o.labels=f,o.cancel=g,o.hasRulesAndActions=h,o.save=i,o.addMailFilterRule=j,o.removeMailFilterRule=k,o.addMailFilterAction=m,o.removeMailFilterAction=n,o.fieldLabels={subject:l("Subject"),from:l("From"),to:l("To"),cc:l("Cc"),to_or_cc:l("To or Cc"),size:l("Size (Kb)"),header:l("Header")},p.indexOf("body")>-1&&(o.fieldLabels.body=l("Body")),o.methodLabels={discard:l("Discard the message"),keep:l("Keep the message"),stop:l("Stop processing filter rules")},q&&(o.methodLabels.redirect=l("Forward the message to")),o.mailboxes=_.map(e,function(a){return{level:a.level,name:a.name,path:a[r]}}),p.indexOf("reject")>-1&&(o.methodLabels.reject=l("Send a reject message")),p.indexOf("fileinto")>-1&&(o.methodLabels.fileinto=l("File the message in")),(p.indexOf("imapflags")>-1||p.indexOf("imap4flags")>-1)&&(o.methodLabels.addflag=l("Flag the message with")),o.numberOperatorLabels={under:l("is under"),over:l("is over")},o.textOperatorLabels={is:l("is"),is_not:l("is not"),contains:l("contains"),contains_not:l("does not contain"),matches:l("matches"),matches_not:l("does not match")},p.indexOf("regex")>-1&&(o.textOperatorLabels.regex=l("matches regex"),o.textOperatorLabels.regex_not=l("does not match regex")),o.flagLabels={seen:l("Seen"),deleted:l("Deleted"),answered:l("Answered"),flagged:l("Flagged"),junk:l("Junk"),not_junk:l("Not Junk")}}a.$inject=["$scope","$window","$mdDialog","filter","mailboxes","labels"],angular.module("SOGo.PreferencesUI").controller("FiltersDialogController",a)}(),function(){"use strict";function a(a,b,c,d,e,f,g,h,i,j,k,m,n,o){function p(a,b){b.$valid&&(d("xs")&&e("left").close(),c.go("preferences."+a))}function q(a){j.confirm(l("Warning"),l("Save preferences and reload page now?"),{ok:l("Yes"),cancel:l("No")}).then(function(){G(a,{quick:!0}).then(function(){b.location.reload(!0)})})}function r(a){Q.preferences.defaults.SOGoCalendarCategoriesColors["New category"]="#aaa",Q.preferences.defaults.SOGoCalendarCategories.push("New category"),i("calendarCategory_"+(Q.preferences.defaults.SOGoCalendarCategories.length-1)),a.$setDirty()}function s(a,b){var c=Q.preferences.defaults.SOGoCalendarCategories[a];Q.preferences.defaults.SOGoCalendarCategories.splice(a,1),delete Q.preferences.defaults.SOGoCalendarCategoriesColors[c],b.$setDirty()}function t(a){Q.preferences.defaults.SOGoContactsCategories.push(""),i("contactCategory_"+(Q.preferences.defaults.SOGoContactsCategories.length-1)),a.$setDirty()}function u(a,b){Q.preferences.defaults.SOGoContactsCategories.splice(a,1),b.$setDirty()}function v(a,b){var c;Q.preferences.defaults.AuxiliaryMailAccounts.push({}),c=_.last(Q.preferences.defaults.AuxiliaryMailAccounts),angular.extend(c,{name:"",identities:[{fullName:"",email:""}],receipts:{receiptAction:"ignore",receiptNonRecipientAction:"ignore",receiptOutsideDomainAction:"ignore",receiptAnyAction:"ignore"}}),f.show({controller:"AccountDialogController",controllerAs:"$AccountDialogController",templateUrl:"editAccount?account=new",targetEvent:a,locals:{defaults:Q.preferences.defaults,account:c,accountId:Q.preferences.defaults.AuxiliaryMailAccounts.length-1,mailCustomFromEnabled:window.mailCustomFromEnabled}}).then(function(){b.$setDirty()}).catch(function(){Q.preferences.defaults.AuxiliaryMailAccounts.pop()})}function w(a,b,c){var d=Q.preferences.defaults.AuxiliaryMailAccounts[b];f.show({controller:"AccountDialogController",controllerAs:"$AccountDialogController",templateUrl:"editAccount?account="+b,targetEvent:a,locals:{defaults:Q.preferences.defaults,account:d,accountId:b,mailCustomFromEnabled:window.mailCustomFromEnabled}}).then(function(){Q.preferences.defaults.AuxiliaryMailAccounts[b]=d,c.$setDirty()})}function x(a,b){Q.preferences.defaults.AuxiliaryMailAccounts.splice(a,1),b.$setDirty()}function y(a){var b="_$$"+guid();Q.preferences.defaults.SOGoMailLabelsColors[b]=["New label","#aaa"],i("mailLabel_"+(_.size(Q.preferences.defaults.SOGoMailLabelsColors)-1)),a.$setDirty()}function z(a,b){delete Q.preferences.defaults.SOGoMailLabelsColors[a],b.$setDirty()}function A(a,b){var c={match:"all"};f.show({templateUrl:"editFilter?filter=new",controller:"FiltersDialogController",controllerAs:"filterEditor",targetEvent:a,locals:{filter:c,mailboxes:R,labels:Q.preferences.defaults.SOGoMailLabelsColors}}).then(function(){Q.preferences.defaults.SOGoSieveFilters||(Q.preferences.defaults.SOGoSieveFilters=[]),Q.preferences.defaults.SOGoSieveFilters.push(c),b.$setDirty()})}function B(a,b,c){var d=angular.copy(Q.preferences.defaults.SOGoSieveFilters[b]);f.show({templateUrl:"editFilter?filter="+b,controller:"FiltersDialogController",controllerAs:"filterEditor",targetEvent:null,locals:{filter:d,mailboxes:R,labels:Q.preferences.defaults.SOGoMailLabelsColors}}).then(function(){Q.preferences.defaults.SOGoSieveFilters[b]=d,c.$setDirty()})}function C(a,b){Q.preferences.defaults.SOGoSieveFilters.splice(a,1),b.$setDirty()}function D(a){var b=[];angular.isDefined(Q.preferences.defaults.Vacation.autoReplyEmailAddresses)&&(b=Q.preferences.defaults.Vacation.autoReplyEmailAddresses.split(",")),Q.preferences.defaults.Vacation.autoReplyEmailAddresses=_.union(window.defaultEmailAddresses.split(","),b).join(","),a.$setDirty()}function E(a,b){return a.length0&&angular.isDefined(Q.preferences.defaults.Forward)&&Q.preferences.defaults.Forward.enabled&&angular.isDefined(Q.preferences.defaults.Forward.forwardAddress))for(h=Q.preferences.defaults.Forward.forwardAddress.split(","),i=b.defaultEmailAddresses.split(/, */),_.forEach(i,function(a){var b=a.split("@")[1];b&&k.push(b.toLowerCase())}),e=0;e=0&&2==b.forwardConstraints&&(j.alert(l("Error"),l("You are not allowed to forward your messages to an internal email address.")),f=!1);return f?Q.preferences.$save().then(function(a){d&&d.quick||(g.show(g.simple().content(l("Preferences saved")).position("bottom right").hideDelay(2e3)),c.$setPristine())}):a.reject()}function H(){return!!(Q.passwords.newPassword&&Q.passwords.newPassword.length>0&&Q.passwords.newPasswordConfirmation&&Q.passwords.newPasswordConfirmation.length&&Q.passwords.newPassword==Q.passwords.newPasswordConfirmation)}function I(){o.changePassword(Q.passwords.newPassword).then(function(){var a=f.alert({title:l("Password"),content:l("The password was changed successfully."),ok:l("OK")});f.show(a).finally(function(){a=void 0})},function(a){var b=f.alert({title:l("Password"),content:a,ok:l("OK")});f.show(b).finally(function(){b=void 0})})}function J(a){return _.filter(Q.timeZonesList,function(b){return b.toUpperCase().indexOf(a.toUpperCase())>=0})}function K(){var a=Q.preferences.defaults;a&&a.Vacation&&a.Vacation.enabled&&(L(),M())}function L(){var a;a=Q.preferences.defaults.Vacation,a.startDateEnabled&&(a.endDateEnabled&&a.startDate.getTime()>a.endDate.getTime()&&(a.startDate=new Date(a.endDate.getTime()),a.startDate.addDays(-1)),a.startDate.getTime()=T.getTime()),c}function O(a){var b=Q.preferences.defaults,c=!0;return b&&b.Vacation&&b.Vacation.enabled&&b.Vacation.endDateEnabled&&(c=(!b.Vacation.startDateEnabled||a.getTime()>b.Vacation.startDate.getTime())&&a.getTime()>=T.getTime()),c}var P,Q=this,R=[],S=new Date,T=S.beginOfDay().addDays(1);Q.preferences=n,Q.passwords={newPassword:null,newPasswordConfirmation:null},Q.go=p,Q.onLanguageChange=q,Q.addCalendarCategory=r,Q.removeCalendarCategory=s,Q.addContactCategory=t,Q.removeContactCategory=u,Q.addMailAccount=v,Q.editMailAccount=w,Q.removeMailAccount=x,Q.addMailLabel=y,Q.removeMailLabel=z,Q.addMailFilter=A,Q.editMailFilter=B,Q.removeMailFilter=C,Q.addDefaultEmailAddresses=D,Q.userFilter=E,Q.confirmChanges=F,Q.save=G,Q.canChangePassword=H,Q.changePassword=I,Q.timeZonesList=window.timeZonesList,Q.timeZonesListFilter=J,Q.timeZonesSearchText="",Q.sieveVariablesCapability=b.sieveCapabilities.indexOf("variables")>=0,Q.updateVacationDates=K,Q.toggleVacationStartDate=L,Q.toggleVacationEndDate=M,Q.validateVacationStartDate=N,Q.validateVacationEndDate=O,P=new m({id:0}),P.$getMailboxes().then(function(){for(var a=P.$flattenMailboxes({all:!0}),b=-1,c=a.length;++b0)&&!e}function g(){a.cancel()}function h(){a.hide()}var i=this;i.defaultPort=143,i.defaults=b,i.account=c,i.accountId=d,i.customFromIsReadonly=f,i.cancel=g,i.save=h,i.account.encryption?"ssl"==i.account.encryption&&(i.defaultPort=993):i.account.encryption="none"}a.$inject=["$mdDialog","defaults","account","accountId","mailCustomFromEnabled"],angular.module("SOGo.PreferencesUI").controller("AccountDialogController",a)}(),function(){"use strict";function a(a,b,c,d,e,f){function g(){c.cancel()}function h(){return o.filter.rules&&o.filter.rules.length>0&&o.filter.actions&&o.filter.actions.length>0}function i(a){c.hide()}function j(a){o.filter.rules||(o.filter.rules=[]),o.filter.rules.push({field:"subject",operator:"contains"})}function k(a){o.filter.rules.splice(a,1)}function m(a){o.filter.actions||(o.filter.actions=[]),o.filter.actions.push({method:"discard"})}function n(a){o.filter.actions.splice(a,1)}var o=this,p=b.sieveCapabilities,q=b.forwardEnabled;b.vacationEnabled;o.filter=d,o.mailboxes=e,o.labels=f,o.cancel=g,o.hasRulesAndActions=h,o.save=i,o.addMailFilterRule=j,o.removeMailFilterRule=k,o.addMailFilterAction=m,o.removeMailFilterAction=n,o.fieldLabels={subject:l("Subject"),from:l("From"),to:l("To"),cc:l("Cc"),to_or_cc:l("To or Cc"),size:l("Size (Kb)"),header:l("Header")},p.indexOf("body")>-1&&(o.fieldLabels.body=l("Body")),o.methodLabels={discard:l("Discard the message"),keep:l("Keep the message"),stop:l("Stop processing filter rules")},q&&(o.methodLabels.redirect=l("Forward the message to")),p.indexOf("reject")>-1&&(o.methodLabels.reject=l("Send a reject message")),p.indexOf("fileinto")>-1&&(o.methodLabels.fileinto=l("File the message in")),(p.indexOf("imapflags")>-1||p.indexOf("imap4flags")>-1)&&(o.methodLabels.addflag=l("Flag the message with")),o.numberOperatorLabels={under:l("is under"),over:l("is over")},o.textOperatorLabels={is:l("is"),is_not:l("is not"),contains:l("contains"),contains_not:l("does not contain"),matches:l("matches"),matches_not:l("does not match")},p.indexOf("regex")>-1&&(o.textOperatorLabels.regex=l("matches regex"),o.textOperatorLabels.regex_not=l("does not match regex")),o.flagLabels={seen:l("Seen"),deleted:l("Deleted"),answered:l("Answered"),flagged:l("Flagged"),junk:l("Junk"),not_junk:l("Not Junk")}}a.$inject=["$scope","$window","$mdDialog","filter","mailboxes","labels"],angular.module("SOGo.PreferencesUI").controller("FiltersDialogController",a)}(),function(){"use strict";function a(a,b,c,d,e,f,g,h,i,j,k,m,n,o){function p(a,b){b.$valid&&(d("xs")&&e("left").close(),c.go("preferences."+a))}function q(a){j.confirm(l("Warning"),l("Save preferences and reload page now?"),{ok:l("Yes"),cancel:l("No")}).then(function(){G(a,{quick:!0}).then(function(){b.location.reload(!0)})})}function r(a){Q.preferences.defaults.SOGoCalendarCategoriesColors["New category"]="#aaa",Q.preferences.defaults.SOGoCalendarCategories.push("New category"),i("calendarCategory_"+(Q.preferences.defaults.SOGoCalendarCategories.length-1)),a.$setDirty()}function s(a,b){var c=Q.preferences.defaults.SOGoCalendarCategories[a];Q.preferences.defaults.SOGoCalendarCategories.splice(a,1),delete Q.preferences.defaults.SOGoCalendarCategoriesColors[c],b.$setDirty()}function t(a){Q.preferences.defaults.SOGoContactsCategories.push(""),i("contactCategory_"+(Q.preferences.defaults.SOGoContactsCategories.length-1)),a.$setDirty()}function u(a,b){Q.preferences.defaults.SOGoContactsCategories.splice(a,1),b.$setDirty()}function v(a,b){var c;Q.preferences.defaults.AuxiliaryMailAccounts.push({}),c=_.last(Q.preferences.defaults.AuxiliaryMailAccounts),angular.extend(c,{name:"",identities:[{fullName:"",email:""}],receipts:{receiptAction:"ignore",receiptNonRecipientAction:"ignore",receiptOutsideDomainAction:"ignore",receiptAnyAction:"ignore"}}),f.show({controller:"AccountDialogController",controllerAs:"$AccountDialogController",templateUrl:"editAccount?account=new",targetEvent:a,locals:{defaults:Q.preferences.defaults,account:c,accountId:Q.preferences.defaults.AuxiliaryMailAccounts.length-1,mailCustomFromEnabled:window.mailCustomFromEnabled}}).then(function(){b.$setDirty()}).catch(function(){Q.preferences.defaults.AuxiliaryMailAccounts.pop()})}function w(a,b,c){var d=Q.preferences.defaults.AuxiliaryMailAccounts[b];f.show({controller:"AccountDialogController",controllerAs:"$AccountDialogController",templateUrl:"editAccount?account="+b,targetEvent:a,locals:{defaults:Q.preferences.defaults,account:d,accountId:b,mailCustomFromEnabled:window.mailCustomFromEnabled}}).then(function(){Q.preferences.defaults.AuxiliaryMailAccounts[b]=d,c.$setDirty()})}function x(a,b){Q.preferences.defaults.AuxiliaryMailAccounts.splice(a,1),b.$setDirty()}function y(a){var b="_$$"+guid();Q.preferences.defaults.SOGoMailLabelsColors[b]=["New label","#aaa"],i("mailLabel_"+(_.size(Q.preferences.defaults.SOGoMailLabelsColors)-1)),a.$setDirty()}function z(a,b){delete Q.preferences.defaults.SOGoMailLabelsColors[a],b.$setDirty()}function A(a,b){var c={match:"all"};f.show({templateUrl:"editFilter?filter=new",controller:"FiltersDialogController",controllerAs:"filterEditor",targetEvent:a,locals:{filter:c,mailboxes:R,labels:Q.preferences.defaults.SOGoMailLabelsColors}}).then(function(){Q.preferences.defaults.SOGoSieveFilters||(Q.preferences.defaults.SOGoSieveFilters=[]),Q.preferences.defaults.SOGoSieveFilters.push(c),b.$setDirty()})}function B(a,b,c){var d=angular.copy(Q.preferences.defaults.SOGoSieveFilters[b]);f.show({templateUrl:"editFilter?filter="+b,controller:"FiltersDialogController",controllerAs:"filterEditor",targetEvent:null,locals:{filter:d,mailboxes:R,labels:Q.preferences.defaults.SOGoMailLabelsColors}}).then(function(){Q.preferences.defaults.SOGoSieveFilters[b]=d,c.$setDirty()})}function C(a,b){Q.preferences.defaults.SOGoSieveFilters.splice(a,1),b.$setDirty()}function D(a){var b=[];angular.isDefined(Q.preferences.defaults.Vacation.autoReplyEmailAddresses)&&(b=Q.preferences.defaults.Vacation.autoReplyEmailAddresses.split(",")),Q.preferences.defaults.Vacation.autoReplyEmailAddresses=_.union(window.defaultEmailAddresses.split(","),b).join(","),a.$setDirty()}function E(a,b){return a.length0&&angular.isDefined(Q.preferences.defaults.Forward)&&Q.preferences.defaults.Forward.enabled&&angular.isDefined(Q.preferences.defaults.Forward.forwardAddress))for(h=Q.preferences.defaults.Forward.forwardAddress.split(","),i=b.defaultEmailAddresses.split(/, */),_.forEach(i,function(a){var b=a.split("@")[1];b&&k.push(b.toLowerCase())}),e=0;e=0&&2==b.forwardConstraints&&(j.alert(l("Error"),l("You are not allowed to forward your messages to an internal email address.")),f=!1);return f?Q.preferences.$save().then(function(a){d&&d.quick||(g.show(g.simple().content(l("Preferences saved")).position("bottom right").hideDelay(2e3)),c.$setPristine())}):a.reject()}function H(){return!!(Q.passwords.newPassword&&Q.passwords.newPassword.length>0&&Q.passwords.newPasswordConfirmation&&Q.passwords.newPasswordConfirmation.length&&Q.passwords.newPassword==Q.passwords.newPasswordConfirmation)}function I(){o.changePassword(Q.passwords.newPassword).then(function(){var a=f.alert({title:l("Password"),content:l("The password was changed successfully."),ok:l("OK")});f.show(a).finally(function(){a=void 0})},function(a){var b=f.alert({title:l("Password"),content:a,ok:l("OK")});f.show(b).finally(function(){b=void 0})})}function J(a){return _.filter(Q.timeZonesList,function(b){return b.toUpperCase().indexOf(a.toUpperCase())>=0})}function K(){var a=Q.preferences.defaults;a&&a.Vacation&&a.Vacation.enabled&&(L(),M())}function L(){var a;a=Q.preferences.defaults.Vacation,a.startDateEnabled&&(a.endDateEnabled&&a.startDate.getTime()>a.endDate.getTime()&&(a.startDate=new Date(a.endDate.getTime()),a.startDate.addDays(-1)),a.startDate.getTime()=T.getTime()),c}function O(a){var b=Q.preferences.defaults,c=!0;return b&&b.Vacation&&b.Vacation.enabled&&b.Vacation.endDateEnabled&&(c=(!b.Vacation.startDateEnabled||a.getTime()>b.Vacation.startDate.getTime())&&a.getTime()>=T.getTime()),c}var P,Q=this,R=[],S=new Date,T=S.beginOfDay().addDays(1);Q.preferences=n,Q.passwords={newPassword:null,newPasswordConfirmation:null},Q.go=p,Q.onLanguageChange=q,Q.addCalendarCategory=r,Q.removeCalendarCategory=s,Q.addContactCategory=t,Q.removeContactCategory=u,Q.addMailAccount=v,Q.editMailAccount=w,Q.removeMailAccount=x,Q.addMailLabel=y,Q.removeMailLabel=z,Q.addMailFilter=A,Q.editMailFilter=B,Q.removeMailFilter=C,Q.addDefaultEmailAddresses=D,Q.userFilter=E,Q.confirmChanges=F,Q.save=G,Q.canChangePassword=H,Q.changePassword=I,Q.timeZonesList=window.timeZonesList,Q.timeZonesListFilter=J,Q.timeZonesSearchText="",Q.sieveVariablesCapability=b.sieveCapabilities.indexOf("variables")>=0,Q.updateVacationDates=K,Q.toggleVacationStartDate=L,Q.toggleVacationEndDate=M,Q.validateVacationStartDate=N,Q.validateVacationEndDate=O,P=new m({id:0}),P.$getMailboxes().then(function(){for(var a=P.$flattenMailboxes({all:!0}),b=-1,c=a.length;++b 0)\n return false;\n\n return !mailCustomFromEnabled;\n }\n\n function cancel() {\n $mdDialog.cancel();\n }\n\n function save() {\n $mdDialog.hide();\n }\n }\n\n angular\n .module('SOGo.PreferencesUI')\n .controller('AccountDialogController', AccountDialogController);\n\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n/* JavaScript for SOGoPreferences */\n\n(function() {\n 'use strict';\n\n /**\n * @ngInject\n */\n FiltersDialogController.$inject = ['$scope', '$window', '$mdDialog', 'filter', 'mailboxes', 'labels'];\n function FiltersDialogController($scope, $window, $mdDialog, filter, mailboxes, labels) {\n var vm = this,\n sieveCapabilities = $window.sieveCapabilities,\n forwardEnabled = $window.forwardEnabled,\n vacationEnabled = $window.vacationEnabled,\n sieveFolderPathAttribute = ($window.sieveFolderEncoding == \"UTF-8\") ? \"name\" : \"path\";\n\n vm.filter = filter;\n vm.mailboxes = mailboxes;\n vm.labels = labels;\n vm.cancel = cancel;\n vm.hasRulesAndActions = hasRulesAndActions;\n vm.save = save;\n vm.addMailFilterRule = addMailFilterRule;\n vm.removeMailFilterRule = removeMailFilterRule;\n vm.addMailFilterAction = addMailFilterAction;\n vm.removeMailFilterAction = removeMailFilterAction;\n\n vm.fieldLabels = {\n \"subject\": l(\"Subject\"),\n \"from\": l(\"From\"),\n \"to\": l(\"To\"),\n \"cc\": l(\"Cc\"),\n \"to_or_cc\": l(\"To or Cc\"),\n \"size\": l(\"Size (Kb)\"),\n \"header\": l(\"Header\")\n };\n\n if (sieveCapabilities.indexOf(\"body\") > -1)\n vm.fieldLabels.body = l(\"Body\");\n\n vm.methodLabels = {\n \"discard\": l(\"Discard the message\"),\n \"keep\": l(\"Keep the message\"),\n \"stop\": l(\"Stop processing filter rules\")\n };\n\n if (forwardEnabled)\n vm.methodLabels.redirect = l(\"Forward the message to\");\n\n //if (vacationEnabled)\n // vm.methodLabels.vacation = l(\"Send a vacation message\");\n\n vm.mailboxes = _.map(mailboxes, function(mailbox) {\n return { level: mailbox.level,\n name: mailbox.name,\n path: mailbox[sieveFolderPathAttribute] };\n });\n\n if (sieveCapabilities.indexOf(\"reject\") > -1)\n vm.methodLabels.reject = l(\"Send a reject message\");\n\n if (sieveCapabilities.indexOf(\"fileinto\") > -1)\n vm.methodLabels.fileinto = l(\"File the message in\");\n\n if (sieveCapabilities.indexOf(\"imapflags\") > -1 || sieveCapabilities.indexOf(\"imap4flags\") > -1)\n vm.methodLabels.addflag = l(\"Flag the message with\");\n\n vm.numberOperatorLabels = {\n \"under\": l(\"is under\"),\n \"over\": l(\"is over\")\n };\n\n vm.textOperatorLabels = {\n \"is\": l(\"is\"),\n \"is_not\": l(\"is not\"),\n \"contains\": l(\"contains\"),\n \"contains_not\": l(\"does not contain\"),\n \"matches\": l(\"matches\"),\n \"matches_not\": l(\"does not match\")\n };\n\n if (sieveCapabilities.indexOf(\"regex\") > -1) {\n vm.textOperatorLabels.regex = l(\"matches regex\");\n vm.textOperatorLabels.regex_not = l(\"does not match regex\");\n }\n\n vm.flagLabels = {\n \"seen\": l(\"Seen\"),\n \"deleted\": l(\"Deleted\"),\n \"answered\": l(\"Answered\"),\n \"flagged\": l(\"Flagged\"),\n \"junk\": l(\"Junk\"),\n \"not_junk\": l(\"Not Junk\")\n };\n \n function cancel() {\n $mdDialog.cancel();\n }\n\n function hasRulesAndActions() {\n return vm.filter.rules && vm.filter.rules.length > 0 &&\n vm.filter.actions && vm.filter.actions.length > 0;\n }\n \n function save(form) {\n $mdDialog.hide();\n }\n\n function addMailFilterRule(event) {\n if (!vm.filter.rules)\n vm.filter.rules = [];\n\n vm.filter.rules.push({ field: 'subject', operator: 'contains' });\n }\n \n function removeMailFilterRule(index) {\n vm.filter.rules.splice(index, 1);\n }\n \n function addMailFilterAction(event) {\n if (!vm.filter.actions)\n vm.filter.actions = [];\n\n vm.filter.actions.push({ method: 'discard' });\n }\n \n function removeMailFilterAction(index) {\n vm.filter.actions.splice(index, 1);\n }\n }\n\n angular\n .module('SOGo.PreferencesUI')\n .controller('FiltersDialogController', FiltersDialogController);\n\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n/* JavaScript for SOGoPreferences */\n\n(function() {\n 'use strict';\n \n /**\n * @ngInject\n */\n PreferencesController.$inject = ['$q', '$window', '$state', '$mdMedia', '$mdSidenav', '$mdDialog', '$mdToast', 'sgSettings', 'sgFocus', 'Dialog', 'User', 'Account', 'statePreferences', 'Authentication'];\n function PreferencesController($q, $window, $state, $mdMedia, $mdSidenav, $mdDialog, $mdToast, sgSettings, focus, Dialog, User, Account, statePreferences, Authentication) {\n var vm = this, account, mailboxes = [], today = new Date(), tomorrow = today.beginOfDay().addDays(1);\n\n vm.preferences = statePreferences;\n vm.passwords = { newPassword: null, newPasswordConfirmation: null };\n\n vm.go = go;\n vm.onLanguageChange = onLanguageChange;\n vm.addCalendarCategory = addCalendarCategory;\n vm.removeCalendarCategory = removeCalendarCategory;\n vm.addContactCategory = addContactCategory;\n vm.removeContactCategory = removeContactCategory;\n vm.addMailAccount = addMailAccount;\n vm.editMailAccount = editMailAccount;\n vm.removeMailAccount = removeMailAccount;\n vm.addMailLabel = addMailLabel;\n vm.removeMailLabel = removeMailLabel;\n vm.addMailFilter = addMailFilter;\n vm.editMailFilter = editMailFilter;\n vm.removeMailFilter = removeMailFilter;\n vm.addDefaultEmailAddresses = addDefaultEmailAddresses;\n vm.userFilter = userFilter;\n vm.confirmChanges = confirmChanges;\n vm.save = save;\n vm.canChangePassword = canChangePassword;\n vm.changePassword = changePassword;\n vm.timeZonesList = window.timeZonesList;\n vm.timeZonesListFilter = timeZonesListFilter;\n vm.timeZonesSearchText = '';\n vm.sieveVariablesCapability = ($window.sieveCapabilities.indexOf('variables') >= 0);\n vm.updateVacationDates = updateVacationDates;\n vm.toggleVacationStartDate = toggleVacationStartDate;\n vm.toggleVacationEndDate = toggleVacationEndDate;\n vm.validateVacationStartDate = validateVacationStartDate;\n vm.validateVacationEndDate = validateVacationEndDate;\n\n\n // Fetch a flatten version of the mailboxes list of the main account (0)\n // This list will be forwarded to the Sieve filter controller\n account = new Account({ id: 0 });\n account.$getMailboxes().then(function() {\n var allMailboxes = account.$flattenMailboxes({all: true}),\n index = -1,\n length = allMailboxes.length;\n while (++index < length) {\n mailboxes.push(allMailboxes[index]);\n }\n });\n\n // Set alternate avatar in User service\n statePreferences.ready().then(function() {\n if (statePreferences.defaults.SOGoAlternateAvatar)\n User.$alternateAvatar = statePreferences.defaults.SOGoAlternateAvatar;\n updateVacationDates();\n });\n\n function go(module, form) {\n if (form.$valid) {\n // Close sidenav on small devices\n if ($mdMedia('xs'))\n $mdSidenav('left').close();\n $state.go('preferences.' + module);\n }\n }\n\n function onLanguageChange(form) {\n Dialog.confirm(l('Warning'),\n l('Save preferences and reload page now?'),\n {ok: l('Yes'), cancel: l('No')})\n .then(function() {\n save(form, { quick: true }).then(function() {\n $window.location.reload(true);\n });\n });\n }\n\n function addCalendarCategory(form) {\n vm.preferences.defaults.SOGoCalendarCategoriesColors[\"New category\"] = \"#aaa\";\n vm.preferences.defaults.SOGoCalendarCategories.push(\"New category\");\n focus('calendarCategory_' + (vm.preferences.defaults.SOGoCalendarCategories.length - 1));\n form.$setDirty();\n }\n\n function removeCalendarCategory(index, form) {\n var key = vm.preferences.defaults.SOGoCalendarCategories[index];\n vm.preferences.defaults.SOGoCalendarCategories.splice(index, 1);\n delete vm.preferences.defaults.SOGoCalendarCategoriesColors[key];\n form.$setDirty();\n }\n\n function addContactCategory(form) {\n vm.preferences.defaults.SOGoContactsCategories.push(\"\");\n focus('contactCategory_' + (vm.preferences.defaults.SOGoContactsCategories.length - 1));\n form.$setDirty();\n }\n\n function removeContactCategory(index, form) {\n vm.preferences.defaults.SOGoContactsCategories.splice(index, 1);\n form.$setDirty();\n }\n\n function addMailAccount(ev, form) {\n var account;\n\n vm.preferences.defaults.AuxiliaryMailAccounts.push({});\n\n account = _.last(vm.preferences.defaults.AuxiliaryMailAccounts);\n angular.extend(account,\n {\n name: \"\",\n identities: [\n {\n fullName: \"\",\n email: \"\"\n }\n ],\n receipts: {\n receiptAction: \"ignore\",\n receiptNonRecipientAction: \"ignore\",\n receiptOutsideDomainAction: \"ignore\",\n receiptAnyAction: \"ignore\"\n }\n });\n\n $mdDialog.show({\n controller: 'AccountDialogController',\n controllerAs: '$AccountDialogController',\n templateUrl: 'editAccount?account=new',\n targetEvent: ev,\n locals: {\n defaults: vm.preferences.defaults,\n account: account,\n accountId: (vm.preferences.defaults.AuxiliaryMailAccounts.length-1),\n mailCustomFromEnabled: window.mailCustomFromEnabled\n }\n }).then(function() {\n form.$setDirty();\n }).catch(function() {\n vm.preferences.defaults.AuxiliaryMailAccounts.pop();\n });\n }\n\n function editMailAccount(event, index, form) {\n var account = vm.preferences.defaults.AuxiliaryMailAccounts[index];\n $mdDialog.show({\n controller: 'AccountDialogController',\n controllerAs: '$AccountDialogController',\n templateUrl: 'editAccount?account=' + index,\n targetEvent: event,\n locals: {\n defaults: vm.preferences.defaults,\n account: account,\n accountId: index,\n mailCustomFromEnabled: window.mailCustomFromEnabled\n }\n }).then(function() {\n vm.preferences.defaults.AuxiliaryMailAccounts[index] = account;\n form.$setDirty();\n });\n }\n\n function removeMailAccount(index, form) {\n vm.preferences.defaults.AuxiliaryMailAccounts.splice(index, 1);\n form.$setDirty();\n }\n \n function addMailLabel(form) {\n // See $omit() in the Preferences services for real key generation\n var key = '_$$' + guid();\n vm.preferences.defaults.SOGoMailLabelsColors[key] = [\"New label\", \"#aaa\"];\n focus('mailLabel_' + (_.size(vm.preferences.defaults.SOGoMailLabelsColors) - 1));\n form.$setDirty();\n }\n\n function removeMailLabel(key, form) {\n delete vm.preferences.defaults.SOGoMailLabelsColors[key];\n form.$setDirty();\n }\n\n function addMailFilter(ev, form) {\n var filter = { match: 'all' };\n\n $mdDialog.show({\n templateUrl: 'editFilter?filter=new',\n controller: 'FiltersDialogController',\n controllerAs: 'filterEditor',\n targetEvent: ev,\n locals: {\n filter: filter,\n mailboxes: mailboxes,\n labels: vm.preferences.defaults.SOGoMailLabelsColors\n }\n }).then(function() {\n if (!vm.preferences.defaults.SOGoSieveFilters)\n vm.preferences.defaults.SOGoSieveFilters = [];\n vm.preferences.defaults.SOGoSieveFilters.push(filter);\n form.$setDirty();\n });\n }\n \n function editMailFilter(ev, index, form) {\n var filter = angular.copy(vm.preferences.defaults.SOGoSieveFilters[index]);\n \n $mdDialog.show({\n templateUrl: 'editFilter?filter=' + index,\n controller: 'FiltersDialogController',\n controllerAs: 'filterEditor',\n targetEvent: null,\n locals: {\n filter: filter,\n mailboxes: mailboxes,\n labels: vm.preferences.defaults.SOGoMailLabelsColors\n }\n }).then(function() {\n vm.preferences.defaults.SOGoSieveFilters[index] = filter;\n form.$setDirty();\n });\n }\n\n function removeMailFilter(index, form) {\n vm.preferences.defaults.SOGoSieveFilters.splice(index, 1);\n form.$setDirty();\n }\n\n function addDefaultEmailAddresses(form) {\n var v = [];\n\n if (angular.isDefined(vm.preferences.defaults.Vacation.autoReplyEmailAddresses)) {\n v = vm.preferences.defaults.Vacation.autoReplyEmailAddresses.split(',');\n }\n\n vm.preferences.defaults.Vacation.autoReplyEmailAddresses = (_.union(window.defaultEmailAddresses.split(','), v)).join(',');\n form.$setDirty();\n }\n\n function userFilter(search, excludedUsers) {\n if (search.length < sgSettings.minimumSearchLength())\n return [];\n\n return User.$filter(search, excludedUsers).then(function(users) {\n // Set users avatars\n _.forEach(users, function(user) {\n if (!user.$$image) {\n if (user.image)\n user.$$image = user.image;\n else\n vm.preferences.avatar(user.c_email, 32, {no_404: true}).then(function(url) {\n user.$$image = url;\n });\n }\n });\n return users;\n });\n }\n\n function confirmChanges($event, form) {\n var target;\n\n if (form.$dirty) {\n // Stop default action\n $event.preventDefault();\n $event.stopPropagation();\n\n // Find target link\n target = $event.target;\n while (target.tagName != 'A')\n target = target.parentNode;\n\n Dialog.confirm(l('Unsaved Changes'),\n l('Do you want to save your changes made to the configuration?'),\n { ok: l('Save'), cancel: l('Don\\'t Save') })\n .then(function() {\n // Save & follow link\n save(form, { quick: true }).then(function() {\n $window.location = target.href;\n });\n }, function() {\n // Don't save & follow link\n $window.location = target.href;\n });\n }\n }\n\n function save(form, options) {\n var i, sendForm, addresses, defaultAddresses, domains, domain;\n\n sendForm = true;\n domains = [];\n\n // We do some sanity checks\n if ($window.forwardConstraints > 0 &&\n angular.isDefined(vm.preferences.defaults.Forward) &&\n vm.preferences.defaults.Forward.enabled &&\n angular.isDefined(vm.preferences.defaults.Forward.forwardAddress)) {\n\n addresses = vm.preferences.defaults.Forward.forwardAddress.split(\",\");\n\n // We first extract the list of 'known domains' to SOGo\n defaultAddresses = $window.defaultEmailAddresses.split(/, */);\n\n _.forEach(defaultAddresses, function(adr) {\n var domain = adr.split(\"@\")[1];\n if (domain) {\n domains.push(domain.toLowerCase());\n }\n });\n\n // We check if we're allowed or not to forward based on the domain defaults\n for (i = 0; i < addresses.length && sendForm; i++) {\n domain = addresses[i].split(\"@\")[1].toLowerCase();\n if (domains.indexOf(domain) < 0 && $window.forwardConstraints == 1) {\n Dialog.alert(l('Error'), l(\"You are not allowed to forward your messages to an external email address.\"));\n sendForm = false;\n }\n else if (domains.indexOf(domain) >= 0 && $window.forwardConstraints == 2) {\n Dialog.alert(l('Error'), l(\"You are not allowed to forward your messages to an internal email address.\"));\n sendForm = false;\n }\n }\n }\n\n if (sendForm)\n return vm.preferences.$save().then(function(data) {\n if (!options || !options.quick) {\n $mdToast.show(\n $mdToast.simple()\n .content(l('Preferences saved'))\n .position('bottom right')\n .hideDelay(2000));\n form.$setPristine();\n }\n });\n\n return $q.reject();\n }\n\n function canChangePassword() {\n if (vm.passwords.newPassword && vm.passwords.newPassword.length > 0 &&\n vm.passwords.newPasswordConfirmation && vm.passwords.newPasswordConfirmation.length &&\n vm.passwords.newPassword == vm.passwords.newPasswordConfirmation)\n return true;\n\n return false;\n }\n \n function changePassword() {\n Authentication.changePassword(vm.passwords.newPassword).then(function() {\n var alert = $mdDialog.alert({\n title: l('Password'),\n content: l('The password was changed successfully.'),\n ok: l('OK')\n });\n $mdDialog.show( alert )\n .finally(function() {\n alert = undefined;\n });\n }, function(msg) {\n var alert = $mdDialog.alert({\n title: l('Password'),\n content: msg,\n ok: l('OK')\n });\n $mdDialog.show( alert )\n .finally(function() {\n alert = undefined;\n });\n });\n }\n\n function timeZonesListFilter(filter) {\n return _.filter(vm.timeZonesList, function(value) {\n return value.toUpperCase().indexOf(filter.toUpperCase()) >= 0;\n });\n }\n\n function updateVacationDates() {\n var d = vm.preferences.defaults;\n\n if (d &&\n d.Vacation &&\n d.Vacation.enabled) {\n toggleVacationStartDate();\n toggleVacationEndDate();\n }\n }\n\n function toggleVacationStartDate() {\n var v;\n\n v = vm.preferences.defaults.Vacation;\n\n if (v.startDateEnabled) {\n // Enabling the start date\n if (v.endDateEnabled && v.startDate.getTime() > v.endDate.getTime()) {\n v.startDate = new Date(v.endDate.getTime());\n v.startDate.addDays(-1);\n }\n if (v.startDate.getTime() < tomorrow.getTime()) {\n v.startDate = new Date(tomorrow.getTime());\n }\n }\n }\n\n function toggleVacationEndDate() {\n var v;\n\n v = vm.preferences.defaults.Vacation;\n\n if (v.endDateEnabled) {\n // Enabling the end date\n if (v.startDateEnabled && v.endDate.getTime() < v.startDate.getTime()) {\n v.endDate = new Date(v.startDate.getTime());\n v.endDate.addDays(1);\n }\n else if (v.endDate.getTime() < tomorrow.getTime()) {\n v.endDate = new Date(tomorrow.getTime());\n }\n }\n }\n\n function validateVacationStartDate(date) {\n var d = vm.preferences.defaults, r = true;\n if (d &&\n d.Vacation &&\n d.Vacation.enabled) {\n if (d.Vacation.startDateEnabled) {\n r = (!d.Vacation.endDateEnabled ||\n date.getTime() < d.Vacation.endDate.getTime()) &&\n date.getTime() >= tomorrow.getTime();\n }\n }\n\n return r;\n }\n\n function validateVacationEndDate(date) {\n var d = vm.preferences.defaults, r = true;\n if (d &&\n d.Vacation &&\n d.Vacation.enabled) {\n if (d.Vacation.endDateEnabled) {\n r = (!d.Vacation.startDateEnabled ||\n date.getTime() > d.Vacation.startDate.getTime()) &&\n date.getTime() >= tomorrow.getTime();\n }\n }\n\n return r;\n }\n }\n\n angular\n .module('SOGo.PreferencesUI')\n .controller('PreferencesController', PreferencesController);\n\n})();\n"]} \ No newline at end of file +{"version":3,"sources":["Preferences/Preferences.app.js","Preferences/AccountDialogController.js","Preferences/FiltersDialogController.js","Preferences/PreferencesController.js"],"names":["configure","$stateProvider","$urlRouterProvider","state","abstract","views","preferences","templateUrl","controller","controllerAs","resolve","statePreferences","url","module","otherwise","Preferences","runBlock","$rootScope","$on","event","current","previous","rejection","console","error","angular","config","run","$inject","AccountDialogController","$mdDialog","defaults","account","accountId","mailCustomFromEnabled","customFromIsReadonly","cancel","save","hide","vm","this","defaultPort","encryption","FiltersDialogController","$scope","$window","filter","mailboxes","labels","hasRulesAndActions","rules","length","actions","form","addMailFilterRule","push","field","operator","removeMailFilterRule","index","splice","addMailFilterAction","method","removeMailFilterAction","sieveCapabilities","forwardEnabled","vacationEnabled","fieldLabels","subject","l","from","to","cc","to_or_cc","size","header","indexOf","body","methodLabels","discard","keep","stop","redirect","reject","fileinto","addflag","numberOperatorLabels","under","over","textOperatorLabels","is","is_not","contains","contains_not","matches","matches_not","regex","regex_not","flagLabels","seen","deleted","answered","flagged","junk","not_junk","PreferencesController","$q","$state","$mdMedia","$mdSidenav","$mdToast","sgSettings","focus","Dialog","User","Account","Authentication","go","$valid","close","onLanguageChange","confirm","ok","then","quick","location","reload","addCalendarCategory","SOGoCalendarCategoriesColors","SOGoCalendarCategories","$setDirty","removeCalendarCategory","key","addContactCategory","SOGoContactsCategories","removeContactCategory","addMailAccount","ev","AuxiliaryMailAccounts","_","last","extend","name","identities","fullName","email","receipts","receiptAction","receiptNonRecipientAction","receiptOutsideDomainAction","receiptAnyAction","show","targetEvent","locals","window","catch","pop","editMailAccount","removeMailAccount","addMailLabel","guid","SOGoMailLabelsColors","removeMailLabel","addMailFilter","match","SOGoSieveFilters","editMailFilter","copy","removeMailFilter","addDefaultEmailAddresses","v","isDefined","Vacation","autoReplyEmailAddresses","split","union","defaultEmailAddresses","join","userFilter","search","excludedUsers","minimumSearchLength","$filter","users","forEach","user","$$image","image","avatar","c_email","no_404","confirmChanges","$event","target","$dirty","preventDefault","stopPropagation","tagName","parentNode","href","options","i","sendForm","addresses","defaultAddresses","domains","domain","forwardConstraints","Forward","enabled","forwardAddress","adr","toLowerCase","alert","$save","data","simple","content","position","hideDelay","$setPristine","canChangePassword","passwords","newPassword","newPasswordConfirmation","changePassword","title","finally","undefined","msg","timeZonesListFilter","timeZonesList","value","toUpperCase","updateVacationDates","d","toggleVacationStartDate","toggleVacationEndDate","startDateEnabled","endDateEnabled","startDate","getTime","endDate","Date","addDays","tomorrow","validateVacationStartDate","date","r","validateVacationEndDate","today","beginOfDay","timeZonesSearchText","sieveVariablesCapability","id","$getMailboxes","allMailboxes","$flattenMailboxes","all","ready","SOGoAlternateAvatar","$alternateAvatar"],"mappings":"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","file":"Preferences.js","sourcesContent":["/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n/* JavaScript for SOGoPreferences */\n\n(function() {\n 'use strict';\n\n angular.module('SOGo.PreferencesUI', ['ui.router', 'ck', 'SOGo.Common', 'SOGo.MailerUI', 'SOGo.ContactsUI', 'SOGo.Authentication', 'as.sortable'])\n .config(configure)\n .run(runBlock);\n\n /**\n * @ngInject\n */\n configure.$inject = ['$stateProvider', '$urlRouterProvider'];\n function configure($stateProvider, $urlRouterProvider) {\n $stateProvider\n .state('preferences', {\n abstract: true,\n views: {\n preferences: {\n templateUrl: 'preferences.html',\n controller: 'PreferencesController',\n controllerAs: 'app'\n }\n },\n resolve: {\n statePreferences: statePreferences\n }\n })\n .state('preferences.general', {\n url: '/general',\n views: {\n module: {\n templateUrl: 'generalPreferences.html'\n }\n }\n })\n .state('preferences.calendars', {\n url: '/calendars',\n views: {\n module: {\n templateUrl: 'calendarsPreferences.html'\n }\n }\n })\n .state('preferences.addressbooks', {\n url: '/addressbooks',\n views: {\n module: {\n templateUrl: 'addressbooksPreferences.html'\n }\n }\n })\n .state('preferences.mailer', {\n url: '/mailer',\n views: {\n module: {\n templateUrl: 'mailerPreferences.html'\n }\n }\n });\n\n // if none of the above states are matched, use this as the fallback\n $urlRouterProvider.otherwise('/general');\n }\n\n /**\n * @ngInject\n */\n statePreferences.$inject = ['Preferences'];\n function statePreferences(Preferences) {\n return Preferences;\n }\n\n /**\n * @ngInject\n */\n runBlock.$inject = ['$rootScope'];\n function runBlock($rootScope) {\n $rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {\n console.error(event, current, previous, rejection);\n });\n }\n\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n/* JavaScript for SOGoPreferences */\n\n(function() {\n 'use strict';\n\n /**\n * @ngInject\n */\n AccountDialogController.$inject = ['$mdDialog', 'defaults', 'account', 'accountId', 'mailCustomFromEnabled'];\n function AccountDialogController($mdDialog, defaults, account, accountId, mailCustomFromEnabled) {\n var vm = this;\n\n vm.defaultPort = 143;\n vm.defaults = defaults;\n vm.account = account;\n vm.accountId = accountId;\n vm.customFromIsReadonly = customFromIsReadonly;\n vm.cancel = cancel;\n vm.save = save;\n\n if (!vm.account.encryption)\n vm.account.encryption = \"none\";\n else if (vm.account.encryption == \"ssl\")\n vm.defaultPort = 993;\n\n function customFromIsReadonly() {\n if (accountId > 0)\n return false;\n\n return !mailCustomFromEnabled;\n }\n\n function cancel() {\n $mdDialog.cancel();\n }\n\n function save() {\n $mdDialog.hide();\n }\n }\n\n angular\n .module('SOGo.PreferencesUI')\n .controller('AccountDialogController', AccountDialogController);\n\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n/* JavaScript for SOGoPreferences */\n\n(function() {\n 'use strict';\n\n /**\n * @ngInject\n */\n FiltersDialogController.$inject = ['$scope', '$window', '$mdDialog', 'filter', 'mailboxes', 'labels'];\n function FiltersDialogController($scope, $window, $mdDialog, filter, mailboxes, labels) {\n var vm = this,\n sieveCapabilities = $window.sieveCapabilities,\n forwardEnabled = $window.forwardEnabled,\n vacationEnabled = $window.vacationEnabled;\n\n vm.filter = filter;\n vm.mailboxes = mailboxes;\n vm.labels = labels;\n vm.cancel = cancel;\n vm.hasRulesAndActions = hasRulesAndActions;\n vm.save = save;\n vm.addMailFilterRule = addMailFilterRule;\n vm.removeMailFilterRule = removeMailFilterRule;\n vm.addMailFilterAction = addMailFilterAction;\n vm.removeMailFilterAction = removeMailFilterAction;\n\n vm.fieldLabels = {\n \"subject\": l(\"Subject\"),\n \"from\": l(\"From\"),\n \"to\": l(\"To\"),\n \"cc\": l(\"Cc\"),\n \"to_or_cc\": l(\"To or Cc\"),\n \"size\": l(\"Size (Kb)\"),\n \"header\": l(\"Header\")\n };\n\n if (sieveCapabilities.indexOf(\"body\") > -1)\n vm.fieldLabels.body = l(\"Body\");\n\n vm.methodLabels = {\n \"discard\": l(\"Discard the message\"),\n \"keep\": l(\"Keep the message\"),\n \"stop\": l(\"Stop processing filter rules\")\n };\n\n if (forwardEnabled)\n vm.methodLabels.redirect = l(\"Forward the message to\");\n\n //if (vacationEnabled)\n // vm.methodLabels.vacation = l(\"Send a vacation message\");\n\n if (sieveCapabilities.indexOf(\"reject\") > -1)\n vm.methodLabels.reject = l(\"Send a reject message\");\n\n if (sieveCapabilities.indexOf(\"fileinto\") > -1)\n vm.methodLabels.fileinto = l(\"File the message in\");\n\n if (sieveCapabilities.indexOf(\"imapflags\") > -1 || sieveCapabilities.indexOf(\"imap4flags\") > -1)\n vm.methodLabels.addflag = l(\"Flag the message with\");\n\n vm.numberOperatorLabels = {\n \"under\": l(\"is under\"),\n \"over\": l(\"is over\")\n };\n\n vm.textOperatorLabels = {\n \"is\": l(\"is\"),\n \"is_not\": l(\"is not\"),\n \"contains\": l(\"contains\"),\n \"contains_not\": l(\"does not contain\"),\n \"matches\": l(\"matches\"),\n \"matches_not\": l(\"does not match\")\n };\n\n if (sieveCapabilities.indexOf(\"regex\") > -1) {\n vm.textOperatorLabels.regex = l(\"matches regex\");\n vm.textOperatorLabels.regex_not = l(\"does not match regex\");\n }\n\n vm.flagLabels = {\n \"seen\": l(\"Seen\"),\n \"deleted\": l(\"Deleted\"),\n \"answered\": l(\"Answered\"),\n \"flagged\": l(\"Flagged\"),\n \"junk\": l(\"Junk\"),\n \"not_junk\": l(\"Not Junk\")\n };\n \n function cancel() {\n $mdDialog.cancel();\n }\n\n function hasRulesAndActions() {\n return vm.filter.rules && vm.filter.rules.length > 0 &&\n vm.filter.actions && vm.filter.actions.length > 0;\n }\n \n function save(form) {\n $mdDialog.hide();\n }\n\n function addMailFilterRule(event) {\n if (!vm.filter.rules)\n vm.filter.rules = [];\n\n vm.filter.rules.push({ field: 'subject', operator: 'contains' });\n }\n \n function removeMailFilterRule(index) {\n vm.filter.rules.splice(index, 1);\n }\n \n function addMailFilterAction(event) {\n if (!vm.filter.actions)\n vm.filter.actions = [];\n\n vm.filter.actions.push({ method: 'discard' });\n }\n \n function removeMailFilterAction(index) {\n vm.filter.actions.splice(index, 1);\n }\n }\n\n angular\n .module('SOGo.PreferencesUI')\n .controller('FiltersDialogController', FiltersDialogController);\n\n})();\n","/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */\n/* JavaScript for SOGoPreferences */\n\n(function() {\n 'use strict';\n \n /**\n * @ngInject\n */\n PreferencesController.$inject = ['$q', '$window', '$state', '$mdMedia', '$mdSidenav', '$mdDialog', '$mdToast', 'sgSettings', 'sgFocus', 'Dialog', 'User', 'Account', 'statePreferences', 'Authentication'];\n function PreferencesController($q, $window, $state, $mdMedia, $mdSidenav, $mdDialog, $mdToast, sgSettings, focus, Dialog, User, Account, statePreferences, Authentication) {\n var vm = this, account, mailboxes = [], today = new Date(), tomorrow = today.beginOfDay().addDays(1);\n\n vm.preferences = statePreferences;\n vm.passwords = { newPassword: null, newPasswordConfirmation: null };\n\n vm.go = go;\n vm.onLanguageChange = onLanguageChange;\n vm.addCalendarCategory = addCalendarCategory;\n vm.removeCalendarCategory = removeCalendarCategory;\n vm.addContactCategory = addContactCategory;\n vm.removeContactCategory = removeContactCategory;\n vm.addMailAccount = addMailAccount;\n vm.editMailAccount = editMailAccount;\n vm.removeMailAccount = removeMailAccount;\n vm.addMailLabel = addMailLabel;\n vm.removeMailLabel = removeMailLabel;\n vm.addMailFilter = addMailFilter;\n vm.editMailFilter = editMailFilter;\n vm.removeMailFilter = removeMailFilter;\n vm.addDefaultEmailAddresses = addDefaultEmailAddresses;\n vm.userFilter = userFilter;\n vm.confirmChanges = confirmChanges;\n vm.save = save;\n vm.canChangePassword = canChangePassword;\n vm.changePassword = changePassword;\n vm.timeZonesList = window.timeZonesList;\n vm.timeZonesListFilter = timeZonesListFilter;\n vm.timeZonesSearchText = '';\n vm.sieveVariablesCapability = ($window.sieveCapabilities.indexOf('variables') >= 0);\n vm.updateVacationDates = updateVacationDates;\n vm.toggleVacationStartDate = toggleVacationStartDate;\n vm.toggleVacationEndDate = toggleVacationEndDate;\n vm.validateVacationStartDate = validateVacationStartDate;\n vm.validateVacationEndDate = validateVacationEndDate;\n\n\n // Fetch a flatten version of the mailboxes list of the main account (0)\n // This list will be forwarded to the Sieve filter controller\n account = new Account({ id: 0 });\n account.$getMailboxes().then(function() {\n var allMailboxes = account.$flattenMailboxes({all: true}),\n index = -1,\n length = allMailboxes.length;\n while (++index < length) {\n mailboxes.push(allMailboxes[index]);\n }\n });\n\n // Set alternate avatar in User service\n statePreferences.ready().then(function() {\n if (statePreferences.defaults.SOGoAlternateAvatar)\n User.$alternateAvatar = statePreferences.defaults.SOGoAlternateAvatar;\n updateVacationDates();\n });\n\n function go(module, form) {\n if (form.$valid) {\n // Close sidenav on small devices\n if ($mdMedia('xs'))\n $mdSidenav('left').close();\n $state.go('preferences.' + module);\n }\n }\n\n function onLanguageChange(form) {\n Dialog.confirm(l('Warning'),\n l('Save preferences and reload page now?'),\n {ok: l('Yes'), cancel: l('No')})\n .then(function() {\n save(form, { quick: true }).then(function() {\n $window.location.reload(true);\n });\n });\n }\n\n function addCalendarCategory(form) {\n vm.preferences.defaults.SOGoCalendarCategoriesColors[\"New category\"] = \"#aaa\";\n vm.preferences.defaults.SOGoCalendarCategories.push(\"New category\");\n focus('calendarCategory_' + (vm.preferences.defaults.SOGoCalendarCategories.length - 1));\n form.$setDirty();\n }\n\n function removeCalendarCategory(index, form) {\n var key = vm.preferences.defaults.SOGoCalendarCategories[index];\n vm.preferences.defaults.SOGoCalendarCategories.splice(index, 1);\n delete vm.preferences.defaults.SOGoCalendarCategoriesColors[key];\n form.$setDirty();\n }\n\n function addContactCategory(form) {\n vm.preferences.defaults.SOGoContactsCategories.push(\"\");\n focus('contactCategory_' + (vm.preferences.defaults.SOGoContactsCategories.length - 1));\n form.$setDirty();\n }\n\n function removeContactCategory(index, form) {\n vm.preferences.defaults.SOGoContactsCategories.splice(index, 1);\n form.$setDirty();\n }\n\n function addMailAccount(ev, form) {\n var account;\n\n vm.preferences.defaults.AuxiliaryMailAccounts.push({});\n\n account = _.last(vm.preferences.defaults.AuxiliaryMailAccounts);\n angular.extend(account,\n {\n name: \"\",\n identities: [\n {\n fullName: \"\",\n email: \"\"\n }\n ],\n receipts: {\n receiptAction: \"ignore\",\n receiptNonRecipientAction: \"ignore\",\n receiptOutsideDomainAction: \"ignore\",\n receiptAnyAction: \"ignore\"\n }\n });\n\n $mdDialog.show({\n controller: 'AccountDialogController',\n controllerAs: '$AccountDialogController',\n templateUrl: 'editAccount?account=new',\n targetEvent: ev,\n locals: {\n defaults: vm.preferences.defaults,\n account: account,\n accountId: (vm.preferences.defaults.AuxiliaryMailAccounts.length-1),\n mailCustomFromEnabled: window.mailCustomFromEnabled\n }\n }).then(function() {\n form.$setDirty();\n }).catch(function() {\n vm.preferences.defaults.AuxiliaryMailAccounts.pop();\n });\n }\n\n function editMailAccount(event, index, form) {\n var account = vm.preferences.defaults.AuxiliaryMailAccounts[index];\n $mdDialog.show({\n controller: 'AccountDialogController',\n controllerAs: '$AccountDialogController',\n templateUrl: 'editAccount?account=' + index,\n targetEvent: event,\n locals: {\n defaults: vm.preferences.defaults,\n account: account,\n accountId: index,\n mailCustomFromEnabled: window.mailCustomFromEnabled\n }\n }).then(function() {\n vm.preferences.defaults.AuxiliaryMailAccounts[index] = account;\n form.$setDirty();\n });\n }\n\n function removeMailAccount(index, form) {\n vm.preferences.defaults.AuxiliaryMailAccounts.splice(index, 1);\n form.$setDirty();\n }\n \n function addMailLabel(form) {\n // See $omit() in the Preferences services for real key generation\n var key = '_$$' + guid();\n vm.preferences.defaults.SOGoMailLabelsColors[key] = [\"New label\", \"#aaa\"];\n focus('mailLabel_' + (_.size(vm.preferences.defaults.SOGoMailLabelsColors) - 1));\n form.$setDirty();\n }\n\n function removeMailLabel(key, form) {\n delete vm.preferences.defaults.SOGoMailLabelsColors[key];\n form.$setDirty();\n }\n\n function addMailFilter(ev, form) {\n var filter = { match: 'all' };\n\n $mdDialog.show({\n templateUrl: 'editFilter?filter=new',\n controller: 'FiltersDialogController',\n controllerAs: 'filterEditor',\n targetEvent: ev,\n locals: {\n filter: filter,\n mailboxes: mailboxes,\n labels: vm.preferences.defaults.SOGoMailLabelsColors\n }\n }).then(function() {\n if (!vm.preferences.defaults.SOGoSieveFilters)\n vm.preferences.defaults.SOGoSieveFilters = [];\n vm.preferences.defaults.SOGoSieveFilters.push(filter);\n form.$setDirty();\n });\n }\n \n function editMailFilter(ev, index, form) {\n var filter = angular.copy(vm.preferences.defaults.SOGoSieveFilters[index]);\n \n $mdDialog.show({\n templateUrl: 'editFilter?filter=' + index,\n controller: 'FiltersDialogController',\n controllerAs: 'filterEditor',\n targetEvent: null,\n locals: {\n filter: filter,\n mailboxes: mailboxes,\n labels: vm.preferences.defaults.SOGoMailLabelsColors\n }\n }).then(function() {\n vm.preferences.defaults.SOGoSieveFilters[index] = filter;\n form.$setDirty();\n });\n }\n\n function removeMailFilter(index, form) {\n vm.preferences.defaults.SOGoSieveFilters.splice(index, 1);\n form.$setDirty();\n }\n\n function addDefaultEmailAddresses(form) {\n var v = [];\n\n if (angular.isDefined(vm.preferences.defaults.Vacation.autoReplyEmailAddresses)) {\n v = vm.preferences.defaults.Vacation.autoReplyEmailAddresses.split(',');\n }\n\n vm.preferences.defaults.Vacation.autoReplyEmailAddresses = (_.union(window.defaultEmailAddresses.split(','), v)).join(',');\n form.$setDirty();\n }\n\n function userFilter(search, excludedUsers) {\n if (search.length < sgSettings.minimumSearchLength())\n return [];\n\n return User.$filter(search, excludedUsers).then(function(users) {\n // Set users avatars\n _.forEach(users, function(user) {\n if (!user.$$image) {\n if (user.image)\n user.$$image = user.image;\n else\n vm.preferences.avatar(user.c_email, 32, {no_404: true}).then(function(url) {\n user.$$image = url;\n });\n }\n });\n return users;\n });\n }\n\n function confirmChanges($event, form) {\n var target;\n\n if (form.$dirty) {\n // Stop default action\n $event.preventDefault();\n $event.stopPropagation();\n\n // Find target link\n target = $event.target;\n while (target.tagName != 'A')\n target = target.parentNode;\n\n Dialog.confirm(l('Unsaved Changes'),\n l('Do you want to save your changes made to the configuration?'),\n { ok: l('Save'), cancel: l('Don\\'t Save') })\n .then(function() {\n // Save & follow link\n save(form, { quick: true }).then(function() {\n $window.location = target.href;\n });\n }, function() {\n // Don't save & follow link\n $window.location = target.href;\n });\n }\n }\n\n function save(form, options) {\n var i, sendForm, addresses, defaultAddresses, domains, domain;\n\n sendForm = true;\n domains = [];\n\n // We do some sanity checks\n if ($window.forwardConstraints > 0 &&\n angular.isDefined(vm.preferences.defaults.Forward) &&\n vm.preferences.defaults.Forward.enabled &&\n angular.isDefined(vm.preferences.defaults.Forward.forwardAddress)) {\n\n addresses = vm.preferences.defaults.Forward.forwardAddress.split(\",\");\n\n // We first extract the list of 'known domains' to SOGo\n defaultAddresses = $window.defaultEmailAddresses.split(/, */);\n\n _.forEach(defaultAddresses, function(adr) {\n var domain = adr.split(\"@\")[1];\n if (domain) {\n domains.push(domain.toLowerCase());\n }\n });\n\n // We check if we're allowed or not to forward based on the domain defaults\n for (i = 0; i < addresses.length && sendForm; i++) {\n domain = addresses[i].split(\"@\")[1].toLowerCase();\n if (domains.indexOf(domain) < 0 && $window.forwardConstraints == 1) {\n Dialog.alert(l('Error'), l(\"You are not allowed to forward your messages to an external email address.\"));\n sendForm = false;\n }\n else if (domains.indexOf(domain) >= 0 && $window.forwardConstraints == 2) {\n Dialog.alert(l('Error'), l(\"You are not allowed to forward your messages to an internal email address.\"));\n sendForm = false;\n }\n }\n }\n\n if (sendForm)\n return vm.preferences.$save().then(function(data) {\n if (!options || !options.quick) {\n $mdToast.show(\n $mdToast.simple()\n .content(l('Preferences saved'))\n .position('bottom right')\n .hideDelay(2000));\n form.$setPristine();\n }\n });\n\n return $q.reject();\n }\n\n function canChangePassword() {\n if (vm.passwords.newPassword && vm.passwords.newPassword.length > 0 &&\n vm.passwords.newPasswordConfirmation && vm.passwords.newPasswordConfirmation.length &&\n vm.passwords.newPassword == vm.passwords.newPasswordConfirmation)\n return true;\n\n return false;\n }\n \n function changePassword() {\n Authentication.changePassword(vm.passwords.newPassword).then(function() {\n var alert = $mdDialog.alert({\n title: l('Password'),\n content: l('The password was changed successfully.'),\n ok: l('OK')\n });\n $mdDialog.show( alert )\n .finally(function() {\n alert = undefined;\n });\n }, function(msg) {\n var alert = $mdDialog.alert({\n title: l('Password'),\n content: msg,\n ok: l('OK')\n });\n $mdDialog.show( alert )\n .finally(function() {\n alert = undefined;\n });\n });\n }\n\n function timeZonesListFilter(filter) {\n return _.filter(vm.timeZonesList, function(value) {\n return value.toUpperCase().indexOf(filter.toUpperCase()) >= 0;\n });\n }\n\n function updateVacationDates() {\n var d = vm.preferences.defaults;\n\n if (d &&\n d.Vacation &&\n d.Vacation.enabled) {\n toggleVacationStartDate();\n toggleVacationEndDate();\n }\n }\n\n function toggleVacationStartDate() {\n var v;\n\n v = vm.preferences.defaults.Vacation;\n\n if (v.startDateEnabled) {\n // Enabling the start date\n if (v.endDateEnabled && v.startDate.getTime() > v.endDate.getTime()) {\n v.startDate = new Date(v.endDate.getTime());\n v.startDate.addDays(-1);\n }\n if (v.startDate.getTime() < tomorrow.getTime()) {\n v.startDate = new Date(tomorrow.getTime());\n }\n }\n }\n\n function toggleVacationEndDate() {\n var v;\n\n v = vm.preferences.defaults.Vacation;\n\n if (v.endDateEnabled) {\n // Enabling the end date\n if (v.startDateEnabled && v.endDate.getTime() < v.startDate.getTime()) {\n v.endDate = new Date(v.startDate.getTime());\n v.endDate.addDays(1);\n }\n else if (v.endDate.getTime() < tomorrow.getTime()) {\n v.endDate = new Date(tomorrow.getTime());\n }\n }\n }\n\n function validateVacationStartDate(date) {\n var d = vm.preferences.defaults, r = true;\n if (d &&\n d.Vacation &&\n d.Vacation.enabled) {\n if (d.Vacation.startDateEnabled) {\n r = (!d.Vacation.endDateEnabled ||\n date.getTime() < d.Vacation.endDate.getTime()) &&\n date.getTime() >= tomorrow.getTime();\n }\n }\n\n return r;\n }\n\n function validateVacationEndDate(date) {\n var d = vm.preferences.defaults, r = true;\n if (d &&\n d.Vacation &&\n d.Vacation.enabled) {\n if (d.Vacation.endDateEnabled) {\n r = (!d.Vacation.startDateEnabled ||\n date.getTime() > d.Vacation.startDate.getTime()) &&\n date.getTime() >= tomorrow.getTime();\n }\n }\n\n return r;\n }\n }\n\n angular\n .module('SOGo.PreferencesUI')\n .controller('PreferencesController', PreferencesController);\n\n})();\n"]} \ No newline at end of file