Expose user's settings and defaults inline

This improves performance by removing two AJAX calls and a lot of
JavaScript promises.
pull/228/merge
Francis Lachapelle 2017-06-01 13:46:57 -04:00
parent 1fa056bb25
commit 89bfa0c536
22 changed files with 441 additions and 484 deletions

1
NEWS
View File

@ -11,6 +11,7 @@ Enhancements
- [web] improve display of tasks status
- [web] added custom fields support from Thunderbird's address book
- [web] added Latvian (lv) translation - thanks to Juris Balandis
- [web] expose user's defaults and settings inline
Bug fixes
- [web] respect SOGoLanguage and SOGoSupportedLanguages (#4169)

View File

@ -33,12 +33,12 @@
};
jsonDefaults = {
protectedBy = "View";
actionClass = "UIxJSONPreferences";
pageName = "UIxJSONPreferences";
actionName = "jsonDefaults";
};
jsonSettings = {
protectedBy = "View";
actionClass = "UIxJSONPreferences";
pageName = "UIxJSONPreferences";
actionName = "jsonSettings";
};
save = {

View File

@ -0,0 +1,12 @@
<?xml version='1.0' standalone='yes'?>
<container
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant">
<script id="UserDefaults" type="text/json">
<var:string value="jsonDefaults" const:escapeHTML="NO"/>
</script>
<script id="UserSettings" type="text/json">
<var:string value="jsonSettings" const:escapeHTML="NO"/>
</script>
</container>

View File

@ -58,16 +58,6 @@
<iframe width="100%" height="100%" src="/SOGo/loading"><!-- space --></iframe>
</div>
</var:if>
<script type="text/javascript">
<var:if condition="shortUserNameForDisplay" const:value="anonymous" const:negate="YES">
<var:if condition="hasUserSettingsKeys">
var UserSettings =<var:string value="userSettings" const:escapeHTML="NO" />;
</var:if>
<var:if condition="hasUserDefaultsKeys">
var UserDefaults = <var:string value="userDefaults" const:escapeHTML="NO" />;
</var:if>
</var:if>
</script>
<!-- MAIN CONTENT ROW -->
<var:component-content />
@ -131,10 +121,15 @@
// This is the equivalent of an AJAX call to /SOGo/so/_UserLogin_/date
var currentDay = <var:string value="currentDayDescription" const:escapeHTML="NO"/>;
<var:string value="commonLocalizableStrings" const:escapeHTML="NO" />
<var:string value="productLocalizableStrings" const:escapeHTML="NO" />
</script>
<var:if condition="shortUserNameForDisplay" const:value="anonymous" const:negate="YES">
<var:component className="UIxJSONPreferences"/>
</var:if>
<!-- JAVASCRIPT IMPORTS -->
<script type="text/javascript" rsrc:src="js/vendor/lodash.min.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular.min.js"><!-- space --></script>

View File

@ -77,18 +77,15 @@
$element.off('click', toggleZoomFcn);
});
// Wait on user's defaults
Preferences.ready().then(function() {
$scope.$watch(function() { return vm.email; }, function(email, old) {
if (email && vm.urlEmail != email) {
// Email has changed or doesn't match the current URL (this happens when using md-virtual-repeat)
showGenericAvatar();
if (Preferences.defaults.SOGoGravatarEnabled)
getGravatar(email);
}
else if (!email)
showGenericAvatar();
});
$scope.$watch(function() { return vm.email; }, function(email, old) {
if (email && vm.urlEmail != email) {
// Email has changed or doesn't match the current URL (this happens when using md-virtual-repeat)
showGenericAvatar();
if (Preferences.defaults.SOGoGravatarEnabled)
getGravatar(email);
}
else if (!email)
showGenericAvatar();
});
// If sg-src is defined, watch the expression for the URL of a local image

View File

@ -49,12 +49,11 @@
$refreshTimeout: null
});
// Initialize sort parameters from user's settings
Preferences.ready().then(function() {
if (Preferences.settings.Contact.SortingState) {
AddressBook.$query.sort = Preferences.settings.Contact.SortingState[0];
AddressBook.$query.asc = parseInt(Preferences.settings.Contact.SortingState[1]);
}
});
if (Preferences.settings.Contact.SortingState) {
AddressBook.$query.sort = Preferences.settings.Contact.SortingState[0];
AddressBook.$query.asc = parseInt(Preferences.settings.Contact.SortingState[1]);
}
return AddressBook; // return constructor
}];
@ -437,19 +436,15 @@
* @desc Starts the refresh timeout for the current selected address book
*/
AddressBook.prototype.$startRefreshTimeout = function() {
var _this = this;
if (AddressBook.$refreshTimeout)
AddressBook.$timeout.cancel(AddressBook.$refreshTimeout);
AddressBook.$Preferences.ready().then(function() {
// Restart the refresh timer, if needed
var refreshViewCheck = AddressBook.$Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually') {
var f = angular.bind(_this, AddressBook.prototype.$reload);
AddressBook.$refreshTimeout = AddressBook.$timeout(f, refreshViewCheck.timeInterval()*1000);
}
});
// Restart the refresh timer, if needed
var refreshViewCheck = AddressBook.$Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually') {
var f = angular.bind(this, AddressBook.prototype.$reload);
AddressBook.$refreshTimeout = AddressBook.$timeout(f, refreshViewCheck.timeInterval()*1000);
}
};
/**
@ -487,91 +482,89 @@
if (!this.isRemote) query.partial = 1;
}
return AddressBook.$Preferences.ready().then(function() {
if (options) {
angular.extend(query, options);
if (dry) {
if (!search) {
// No query specified
_this.$$cards = [];
return AddressBook.$q.when(_this.$$cards);
}
if (options) {
angular.extend(query, options);
if (dry) {
if (!search) {
// No query specified
_this.$$cards = [];
return AddressBook.$q.when(_this.$$cards);
}
}
}
if (angular.isDefined(search))
query.value = search;
if (angular.isDefined(search))
query.value = search;
return _this.$id().then(function(addressbookId) {
var futureData = AddressBook.$$resource.fetch(addressbookId, 'view', query);
return _this.$id().then(function(addressbookId) {
var futureData = AddressBook.$$resource.fetch(addressbookId, 'view', query);
if (dry) {
return futureData.then(function(response) {
var results, headers, card, index, fields, idFieldIndex,
cards = _this.$$cards,
compareIds = function(card) {
return this == card.id;
};
if (dry) {
return futureData.then(function(response) {
var results, headers, card, index, fields, idFieldIndex,
cards = _this.$$cards,
compareIds = function(card) {
return this == card.id;
};
if (response.headers) {
// First entry of 'headers' are keys
fields = _.invokeMap(response.headers[0], 'toLowerCase');
idFieldIndex = fields.indexOf('id');
response.headers.splice(0, 1);
if (response.headers) {
// First entry of 'headers' are keys
fields = _.invokeMap(response.headers[0], 'toLowerCase');
idFieldIndex = fields.indexOf('id');
response.headers.splice(0, 1);
}
if (excludedCards)
// Remove excluded cards from results
results = _.filter(response.ids, function(id) {
return _.isUndefined(_.find(excludedCards, _.bind(compareIds, id)));
});
else
results = response.ids;
// Remove cards that no longer match the search query
for (index = cards.length - 1; index >= 0; index--) {
card = cards[index];
if (_.isUndefined(_.find(results, _.bind(compareIds, card.id)))) {
cards.splice(index, 1);
}
}
if (excludedCards)
// Remove excluded cards from results
results = _.filter(response.ids, function(id) {
return _.isUndefined(_.find(excludedCards, _.bind(compareIds, id)));
});
else
results = response.ids;
// Remove cards that no longer match the search query
for (index = cards.length - 1; index >= 0; index--) {
card = cards[index];
if (_.isUndefined(_.find(results, _.bind(compareIds, card.id)))) {
cards.splice(index, 1);
}
// Add new cards matching the search query
_.forEach(results, function(cardId, index) {
if (_.isUndefined(_.find(cards, _.bind(compareIds, cardId)))) {
var data = { pid: addressbookId, id: cardId };
var card = new AddressBook.$Card(data, search);
cards.splice(index, 0, card);
}
// Add new cards matching the search query
_.forEach(results, function(cardId, index) {
if (_.isUndefined(_.find(cards, _.bind(compareIds, cardId)))) {
var data = { pid: addressbookId, id: cardId };
var card = new AddressBook.$Card(data, search);
cards.splice(index, 0, card);
}
});
// Respect the order of the results
_.forEach(results, function(cardId, index) {
var oldIndex, removedCards;
if (cards[index].id != cardId) {
oldIndex = _.findIndex(cards, _.bind(compareIds, cardId));
removedCards = cards.splice(oldIndex, 1);
cards.splice(index, 0, removedCards[0]);
}
});
// Extend Card objects with received headers
_.forEach(response.headers, function(data) {
var card, index = _.findIndex(cards, _.bind(compareIds, data[idFieldIndex]));
if (index > -1) {
card = _.zipObject(fields, data);
cards[index].init(card, search);
}
});
return cards;
});
}
else {
// Unwrap promise and instantiate or extend Cards objets
return _this.$unwrap(futureData);
}
});
// Respect the order of the results
_.forEach(results, function(cardId, index) {
var oldIndex, removedCards;
if (cards[index].id != cardId) {
oldIndex = _.findIndex(cards, _.bind(compareIds, cardId));
removedCards = cards.splice(oldIndex, 1);
cards.splice(index, 0, removedCards[0]);
}
});
// Extend Card objects with received headers
_.forEach(response.headers, function(data) {
var card, index = _.findIndex(cards, _.bind(compareIds, data[idFieldIndex]));
if (index > -1) {
card = _.zipObject(fields, data);
cards[index].init(card, search);
}
});
return cards;
});
}
else {
// Unwrap promise and instantiate or extend Cards objets
return _this.$unwrap(futureData);
}
});
};

View File

@ -46,13 +46,11 @@
$Preferences: Preferences
});
// Initialize categories from user's defaults
Preferences.ready().then(function() {
if (Preferences.defaults.SOGoContactsCategories) {
Card.$categories = Preferences.defaults.SOGoContactsCategories;
}
if (Preferences.defaults.SOGoAlternateAvatar)
Card.$alternateAvatar = Preferences.defaults.SOGoAlternateAvatar;
});
if (Preferences.defaults.SOGoContactsCategories) {
Card.$categories = Preferences.defaults.SOGoContactsCategories;
}
if (Preferences.defaults.SOGoAlternateAvatar)
Card.$alternateAvatar = Preferences.defaults.SOGoAlternateAvatar;
return Card; // return constructor
}];
@ -149,9 +147,7 @@
if (!this.$$image)
this.$$image = this.image;
if (!this.$$image)
Card.$Preferences.avatar(this.$$email, 32, {no_404: true}).then(function(url) {
_this.$$image = url;
});
this.$$image = Card.$Preferences.avatar(this.$$email, 32, {no_404: true});
if (this.hasphoto)
this.photoURL = Card.$$resource.path(this.pid, this.id, 'photo');
if (this.isgroup)
@ -177,11 +173,9 @@
});
// Instanciate date object of birthday
if (this.birthday) {
Card.$Preferences.ready().then(function() {
var dlp = Card.$Preferences.$mdDateLocaleProvider;
_this.birthday = _this.birthday.parseDate(dlp, '%Y-%m-%d');
_this.$birthday = dlp.formatDate(_this.birthday);
});
var dlp = Card.$Preferences.$mdDateLocaleProvider;
this.birthday = this.birthday.parseDate(dlp, '%Y-%m-%d');
this.$birthday = dlp.formatDate(this.birthday);
}
this.$loaded = angular.isDefined(this.c_name)? Card.STATUS.LOADED : Card.STATUS.NOT_LOADED;

View File

@ -136,31 +136,29 @@
_this.$expanded = false;
// Set expanded folders from user's settings
Account.$Preferences.ready().then(function() {
var expandedFolders,
_visit = function(mailboxes) {
_.forEach(mailboxes, function(o) {
o.$expanded = (expandedFolders.indexOf('/' + o.id) >= 0);
if (o.children && o.children.length > 0) {
_visit(o.children);
}
});
};
if (Account.$Preferences.settings.Mail.ExpandedFolders) {
if (angular.isString(Account.$Preferences.settings.Mail.ExpandedFolders))
// Backward compatibility support
expandedFolders = angular.fromJson(Account.$Preferences.settings.Mail.ExpandedFolders);
else
expandedFolders = Account.$Preferences.settings.Mail.ExpandedFolders;
_this.$expanded = (expandedFolders.indexOf('/' + _this.id) >= 0);
if (expandedFolders.length > 0) {
_visit(_this.$mailboxes);
}
var expandedFolders,
_visit = function(mailboxes) {
_.forEach(mailboxes, function(o) {
o.$expanded = (expandedFolders.indexOf('/' + o.id) >= 0);
if (o.children && o.children.length > 0) {
_visit(o.children);
}
});
};
if (Account.$Preferences.settings.Mail.ExpandedFolders) {
if (angular.isString(Account.$Preferences.settings.Mail.ExpandedFolders))
// Backward compatibility support
expandedFolders = angular.fromJson(Account.$Preferences.settings.Mail.ExpandedFolders);
else
expandedFolders = Account.$Preferences.settings.Mail.ExpandedFolders;
_this.$expanded = (expandedFolders.indexOf('/' + _this.id) >= 0);
if (expandedFolders.length > 0) {
_visit(_this.$mailboxes);
}
if (Account.$accounts)
_this.$expanded |= (Account.$accounts.length == 1); // Always expand single account
_this.$flattenMailboxes({reload: true});
});
}
if (Account.$accounts)
_this.$expanded |= (Account.$accounts.length == 1); // Always expand single account
_this.$flattenMailboxes({reload: true});
return _this.$mailboxes;
});

View File

@ -48,12 +48,10 @@
PRELOAD: PRELOAD
});
// Initialize sort parameters from user's settings
Preferences.ready().then(function() {
if (Preferences.settings.Mail.SortingState) {
Mailbox.$query.sort = Preferences.settings.Mail.SortingState[0];
Mailbox.$query.asc = parseInt(Preferences.settings.Mail.SortingState[1]);
}
});
if (Preferences.settings.Mail.SortingState) {
Mailbox.$query.sort = Preferences.settings.Mail.SortingState[0];
Mailbox.$query.asc = parseInt(Preferences.settings.Mail.SortingState[1]);
}
return Mailbox; // return constructor
}];
@ -343,46 +341,43 @@
_this.$isLoading = true;
});
return Mailbox.$Preferences.ready().then(function() {
if (Mailbox.$refreshTimeout)
Mailbox.$timeout.cancel(Mailbox.$refreshTimeout);
if (Mailbox.$refreshTimeout)
Mailbox.$timeout.cancel(Mailbox.$refreshTimeout);
if (sortingAttributes)
// Sorting preferences are common to all mailboxes
angular.extend(Mailbox.$query, sortingAttributes);
if (sortingAttributes)
// Sorting preferences are common to all mailboxes
angular.extend(Mailbox.$query, sortingAttributes);
angular.extend(options, { sortingAttributes: Mailbox.$query });
if (angular.isDefined(filters)) {
options.filters = _.reject(filters, function(filter) {
return !filter.searchInput || filter.searchInput.length === 0;
});
// Decompose filters that match two fields
_.forEach(options.filters, function(filter) {
var secondFilter,
match = filter.searchBy.match(/(\w+)_or_(\w+)/);
if (match) {
options.sortingAttributes.match = 'OR';
filter.searchBy = match[1];
secondFilter = angular.copy(filter);
secondFilter.searchBy = match[2];
options.filters.push(secondFilter);
}
});
}
// Restart the refresh timer, if needed
if (!Mailbox.$virtualMode) {
var refreshViewCheck = Mailbox.$Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually') {
var f = angular.bind(_this, Mailbox.prototype.$filter, null, filters);
Mailbox.$refreshTimeout = Mailbox.$timeout(f, refreshViewCheck.timeInterval()*1000);
angular.extend(options, { sortingAttributes: Mailbox.$query });
if (angular.isDefined(filters)) {
options.filters = _.reject(filters, function(filter) {
return !filter.searchInput || filter.searchInput.length === 0;
});
// Decompose filters that match two fields
_.forEach(options.filters, function(filter) {
var secondFilter,
match = filter.searchBy.match(/(\w+)_or_(\w+)/);
if (match) {
options.sortingAttributes.match = 'OR';
filter.searchBy = match[1];
secondFilter = angular.copy(filter);
secondFilter.searchBy = match[2];
options.filters.push(secondFilter);
}
}
});
}
var futureMailboxData = Mailbox.$$resource.post(_this.id, 'view', options);
return _this.$unwrap(futureMailboxData);
});
// Restart the refresh timer, if needed
if (!Mailbox.$virtualMode) {
var refreshViewCheck = Mailbox.$Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually') {
var f = angular.bind(this, Mailbox.prototype.$filter, null, filters);
Mailbox.$refreshTimeout = Mailbox.$timeout(f, refreshViewCheck.timeInterval()*1000);
}
}
var futureMailboxData = Mailbox.$$resource.post(this.id, 'view', options);
return this.$unwrap(futureMailboxData);
};
/**

View File

@ -32,9 +32,7 @@
params: []
};
Preferences.ready().then(function() {
vm.showSubscribedOnly = Preferences.defaults.SOGoMailShowSubscribedFoldersOnly;
});
this.showSubscribedOnly = Preferences.defaults.SOGoMailShowSubscribedFoldersOnly;
this.refreshUnseenCount();
@ -258,7 +256,7 @@
}; // delegate
this.refreshUnseenCount = function() {
var unseenCountFolders = $window.unseenCountFolders;
var unseenCountFolders = $window.unseenCountFolders, refreshViewCheck;
_.forEach(vm.accounts, function(account) {
@ -282,11 +280,9 @@
});
});
Preferences.ready().then(function() {
var refreshViewCheck = Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually')
$timeout(vm.refreshUnseenCount, refreshViewCheck.timeInterval()*1000);
});
refreshViewCheck = Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually')
$timeout(vm.refreshUnseenCount, refreshViewCheck.timeInterval()*1000);
};
this.isDroppableFolder = function(srcFolder, dstFolder) {

View File

@ -48,16 +48,15 @@
$$resource: new Resource(Settings.activeUser('folderURL') + 'Mail', Settings.activeUser()),
$avatar: angular.bind(Preferences, Preferences.avatar)
});
// Initialize tags form user's defaults
Preferences.ready().then(function() {
if (Preferences.defaults.SOGoMailLabelsColors) {
Message.$tags = Preferences.defaults.SOGoMailLabelsColors;
}
if (Preferences.defaults.SOGoMailDisplayRemoteInlineImages &&
Preferences.defaults.SOGoMailDisplayRemoteInlineImages == 'always') {
Message.$displayRemoteInlineImages = true;
}
});
if (Preferences.defaults.SOGoMailLabelsColors) {
Message.$tags = Preferences.defaults.SOGoMailLabelsColors;
}
if (Preferences.defaults.SOGoMailDisplayRemoteInlineImages &&
Preferences.defaults.SOGoMailDisplayRemoteInlineImages == 'always') {
Message.$displayRemoteInlineImages = true;
}
return Message; // return constructor
}];

View File

@ -65,6 +65,13 @@
}
});
// Read user's defaults
if (Preferences.defaults.SOGoMailAutoSave)
// Enable auto-save of draft
vm.autosave = $timeout(vm.autosaveDrafts, Preferences.defaults.SOGoMailAutoSave*1000*60);
// Set the locale of CKEditor
vm.localeCode = Preferences.defaults.LocaleCode;
// Destroy file uploader when the controller is being deactivated
$scope.$on('$destroy', function() { vm.uploader.destroy(); });
@ -297,14 +304,6 @@
vm.autosave = $timeout(vm.autosaveDrafts, Preferences.defaults.SOGoMailAutoSave*1000*60);
}
// Read user's defaults
Preferences.ready().then(function() {
if (Preferences.defaults.SOGoMailAutoSave)
// Enable auto-save of draft
vm.autosave = $timeout(vm.autosaveDrafts, Preferences.defaults.SOGoMailAutoSave*1000*60);
// Set the locale of CKEditor
vm.localeCode = Preferences.defaults.LocaleCode;
});
}
SendMessageToastController.$inject = ['$scope', '$mdToast'];

View File

@ -22,9 +22,6 @@
controller: 'PreferencesController',
controllerAs: 'app'
}
},
resolve: {
statePreferences: statePreferences
}
})
.state('preferences.general', {
@ -64,13 +61,6 @@
$urlRouterProvider.otherwise('/general');
}
/**
* @ngInject
*/
statePreferences.$inject = ['Preferences'];
function statePreferences(Preferences) {
return Preferences;
}
/**
* @ngInject

View File

@ -8,13 +8,19 @@
* @constructor
*/
function Preferences() {
var _this = this;
var _this = this, defaultsElement, settingsElement, data;
this.defaults = {};
this.settings = {};
this.defaultsPromise = Preferences.$$resource.fetch("jsonDefaults").then(function(response) {
var data = response || {};
defaultsElement = Preferences.$document[0].getElementById('UserDefaults');
if (defaultsElement) {
try {
data = angular.fromJson(defaultsElement.textContent || defaultsElement.innerHTML);
} catch (e) {
Preferences.$log.error("Can't parse user's defaults: " + e.message);
data = {};
}
// We swap $key -> _$key to avoid an Angular bug (https://github.com/angular/angular.js/issues/6266)
var labels = _.fromPairs(_.map(data.SOGoMailLabelsColors, function(value, key) {
@ -117,22 +123,28 @@
_this.$mdDateLocaleProvider.msgCalendar = l('Calender');
_this.$mdDateLocaleProvider.msgOpenCalendar = l('Open Calendar');
_this.$mdDateLocaleProvider.parseDate = function(dateString) {
return dateString? dateString.parseDate(_this.$mdDateLocaleProvider, data.SOGoShortDateFormat) : new Date(NaN);
return dateString? dateString.parseDate(_this.$mdDateLocaleProvider, _this.defaults.SOGoShortDateFormat) : new Date(NaN);
};
_this.$mdDateLocaleProvider.formatDate = function(date) {
return date? date.format(_this.$mdDateLocaleProvider, date.$dateFormat || data.SOGoShortDateFormat) : '';
return date? date.format(_this.$mdDateLocaleProvider, date.$dateFormat || _this.defaults.SOGoShortDateFormat) : '';
};
_this.$mdDateLocaleProvider.parseTime = function(timeString) {
return timeString? timeString.parseDate(_this.$mdDateLocaleProvider, data.SOGoTimeFormat) : new Date(NaN);
return timeString? timeString.parseDate(_this.$mdDateLocaleProvider, _this.defaults.SOGoTimeFormat) : new Date(NaN);
};
_this.$mdDateLocaleProvider.formatTime = function(date) {
return date? date.format(_this.$mdDateLocaleProvider, data.SOGoTimeFormat) : '';
return date? date.format(_this.$mdDateLocaleProvider, _this.defaults.SOGoTimeFormat) : '';
};
}
return _this.defaults;
});
settingsElement = Preferences.$document[0].getElementById('UserSettings');
if (settingsElement) {
try {
data = angular.fromJson(settingsElement.textContent || settingsElement.innerHTML);
} catch (e) {
Preferences.$log.error("Can't parse user's settings: " + e.message);
data = {};
}
this.settingsPromise = Preferences.$$resource.fetch("jsonSettings").then(function(data) {
// We convert our PreventInvitationsWhitelist hash into a array of user
if (data.Calendar) {
if (data.Calendar.PreventInvitationsWhitelist) {
@ -140,9 +152,7 @@
var match = /^(.+)\s<(\S+)>$/.exec(value),
user = new Preferences.$User({uid: key, cn: match[1], c_email: match[2]});
if (!user.$$image)
_this.avatar(user.c_email, 32, {no_404: true}).then(function(url) {
user.$$image = url;
});
user.$$image = _this.avatar(user.c_email, 32, {no_404: true});
return user;
});
}
@ -151,9 +161,7 @@
}
angular.extend(_this.settings, data);
return _this.settings;
});
}
}
/**
@ -161,8 +169,9 @@
* @desc The factory we'll use to register with Angular
* @returns the Preferences constructor
*/
Preferences.$factory = ['$q', '$timeout', '$log', '$mdDateLocale', 'sgSettings', 'Gravatar', 'Resource', 'User', function($q, $timeout, $log, $mdDateLocaleProvider, Settings, Gravatar, Resource, User) {
Preferences.$factory = ['$document', '$q', '$timeout', '$log', '$mdDateLocale', 'sgSettings', 'Gravatar', 'Resource', 'User', function($document, $q, $timeout, $log, $mdDateLocaleProvider, Settings, Gravatar, Resource, User) {
angular.extend(Preferences, {
$document: $document,
$q: $q,
$timeout: $timeout,
$log: $log,
@ -195,7 +204,8 @@
* @return a combined promise
*/
Preferences.prototype.ready = function() {
return Preferences.$q.all([this.defaultsPromise, this.settingsPromise]);
Preferences.$log.warn('Preferences.ready is deprecated -- access settings/defaults directly.');
return Preferences.$q.when(true);
};
/**
@ -206,16 +216,14 @@
*/
Preferences.prototype.avatar = function(email, size, options) {
var _this = this;
return this.ready().then(function() {
var alternate_avatar = _this.defaults.SOGoAlternateAvatar, url;
if (_this.defaults.SOGoGravatarEnabled)
url = Preferences.$gravatar(email, size, alternate_avatar, options);
else
url = [Preferences.$resourcesURL, 'img', 'ic_person_grey_24px.svg'].join('/');
if (options && options.dstObject && options.dstAttr)
options.dstObject[options.dstAttr] = url;
return url;
});
var alternate_avatar = _this.defaults.SOGoAlternateAvatar, url;
if (_this.defaults.SOGoGravatarEnabled)
url = Preferences.$gravatar(email, size, alternate_avatar, options);
else
url = [Preferences.$resourcesURL, 'img', 'ic_person_grey_24px.svg'].join('/');
if (options && options.dstObject && options.dstAttr)
options.dstObject[options.dstAttr] = url;
return url;
};
/**
@ -285,12 +293,16 @@
if (preferences.defaults.Vacation) {
if (preferences.defaults.Vacation.startDateEnabled)
preferences.defaults.Vacation.startDate = preferences.defaults.Vacation.startDate.getTime()/1000;
else
else {
delete preferences.defaults.Vacation.startDateEnabled;
preferences.defaults.Vacation.startDate = 0;
}
if (preferences.defaults.Vacation.endDateEnabled)
preferences.defaults.Vacation.endDate = preferences.defaults.Vacation.endDate.getTime()/1000;
else
else {
delete preferences.defaults.Vacation.endDateEnabled;
preferences.defaults.Vacation.endDate = 0;
}
if (preferences.defaults.Vacation.autoReplyEmailAddresses)
preferences.defaults.Vacation.autoReplyEmailAddresses = _.filter(preferences.defaults.Vacation.autoReplyEmailAddresses.split(","), function(v) { return v.length; });

View File

@ -7,11 +7,11 @@
/**
* @ngInject
*/
PreferencesController.$inject = ['$q', '$window', '$state', '$mdMedia', '$mdSidenav', '$mdDialog', '$mdToast', 'sgSettings', 'sgFocus', 'Dialog', 'User', 'Account', 'statePreferences', 'Authentication'];
function PreferencesController($q, $window, $state, $mdMedia, $mdSidenav, $mdDialog, $mdToast, sgSettings, focus, Dialog, User, Account, statePreferences, Authentication) {
PreferencesController.$inject = ['$q', '$window', '$state', '$mdMedia', '$mdSidenav', '$mdDialog', '$mdToast', 'sgSettings', 'sgFocus', 'Dialog', 'User', 'Account', 'Preferences', 'Authentication'];
function PreferencesController($q, $window, $state, $mdMedia, $mdSidenav, $mdDialog, $mdToast, sgSettings, focus, Dialog, User, Account, Preferences, Authentication) {
var vm = this, account, mailboxes = [], today = new Date(), tomorrow = today.beginOfDay().addDays(1);
vm.preferences = statePreferences;
vm.preferences = Preferences;
vm.passwords = { newPassword: null, newPasswordConfirmation: null };
vm.go = go;
@ -60,11 +60,9 @@
}
// Set alternate avatar in User service
statePreferences.ready().then(function() {
if (statePreferences.defaults.SOGoAlternateAvatar)
User.$alternateAvatar = statePreferences.defaults.SOGoAlternateAvatar;
updateVacationDates();
});
if (Preferences.defaults.SOGoAlternateAvatar)
User.$alternateAvatar = Preferences.defaults.SOGoAlternateAvatar;
updateVacationDates();
function go(module, form) {
if (form.$valid) {

View File

@ -102,11 +102,9 @@
else
list.splice(sibling, 0, calendar);
this.$Preferences.ready().then(function() {
if (Calendar.$Preferences.settings.Calendar.FoldersOrder)
// Save list order
Calendar.saveFoldersOrder(_.flatMap(Calendar.$findAll(), 'id'));
});
if (Calendar.$Preferences.settings.Calendar.FoldersOrder)
// Save list order
Calendar.saveFoldersOrder(_.flatMap(Calendar.$findAll(), 'id'));
// Refresh list of calendars to fetch links associated to new calendar
Calendar.$reloadAll();
};

View File

@ -23,23 +23,23 @@
vm.changeView = changeView;
_registerHotkeys(hotkeys);
this.$onInit = function() {
_registerHotkeys(hotkeys);
Preferences.ready().then(function() {
_formatDate(vm.selectedDate);
});
// Refresh current view when the list of calendars is modified
deregisterCalendarsList = $rootScope.$on('calendars:list', updateView);
// Refresh current view when the list of calendars is modified
deregisterCalendarsList = $rootScope.$on('calendars:list', updateView);
$scope.$on('$destroy', function() {
// Destroy event listener when the controller is being deactivated
deregisterCalendarsList();
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
$scope.$on('$destroy', function() {
// Destroy event listener when the controller is being deactivated
deregisterCalendarsList();
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
});
};
function _registerHotkeys(keys) {

View File

@ -8,7 +8,7 @@
*/
CalendarListController.$inject = ['$rootScope', '$scope', '$timeout', '$state', '$mdDialog', 'sgHotkeys', 'sgFocus', 'Dialog', 'Preferences', 'CalendarSettings', 'Calendar', 'Component', 'Alarm'];
function CalendarListController($rootScope, $scope, $timeout, $state, $mdDialog, sgHotkeys, focus, Dialog, Preferences, CalendarSettings, Calendar, Component, Alarm) {
var vm = this, hotkeys = [];
var vm = this, hotkeys = [], type;
vm.component = Component;
vm.componentType = 'events';
@ -32,32 +32,32 @@
vm.mode = { search: false, multiple: 0 };
_registerHotkeys(hotkeys);
this.$onInit = function() {
_registerHotkeys(hotkeys);
// Select list based on user's settings
Preferences.ready().then(function() {
var type = 'events';
// Select list based on user's settings
type = 'events';
if (Preferences.settings.Calendar.SelectedList == 'tasksListView') {
vm.selectedList = 1;
type = 'tasks';
}
selectComponentType(type, { reload: true }); // fetch events/tasks lists
});
// Refresh current list when the list of calendars is modified
$rootScope.$on('calendars:list', function() {
Component.$filter(vm.componentType, { reload: true });
});
// Update the component being dragged
$rootScope.$on('calendar:dragend', updateComponentFromGhost);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
// Refresh current list when the list of calendars is modified
$rootScope.$on('calendars:list', function() {
Component.$filter(vm.componentType, { reload: true });
});
});
// Update the component being dragged
$rootScope.$on('calendar:dragend', updateComponentFromGhost);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
};
function _registerHotkeys(keys) {

View File

@ -38,47 +38,47 @@
accept: _sortableAccept
};
Preferences.ready().then(function() {
this.$onInit = function() {
vm.categories = _.map(Preferences.defaults.SOGoCalendarCategories, function(name) {
return { id: name.asCSSIdentifier(),
name: name,
color: Preferences.defaults.SOGoCalendarCategoriesColors[name]
};
});
});
// Dispatch the event named 'calendars:list' when a calendar is activated or deactivated or
// when the color of a calendar is changed
$scope.$watch(
function() {
return _.union(
_.map(Calendar.$calendars, function(o) { return _.pick(o, ['id', 'active', 'color']); }),
_.map(Calendar.$subscriptions, function(o) { return _.pick(o, ['id', 'active', 'color']); }),
_.map(Calendar.$webcalendars, function(o) { return _.pick(o, ['id', 'active', 'color']); })
);
},
function(newList, oldList) {
var commonList, ids, promise;
// Dispatch the event named 'calendars:list' when a calendar is activated or deactivated or
// when the color of a calendar is changed
$scope.$watch(
function() {
return _.union(
_.map(Calendar.$calendars, function(o) { return _.pick(o, ['id', 'active', 'color']); }),
_.map(Calendar.$subscriptions, function(o) { return _.pick(o, ['id', 'active', 'color']); }),
_.map(Calendar.$webcalendars, function(o) { return _.pick(o, ['id', 'active', 'color']); })
);
},
function(newList, oldList) {
var commonList, ids, promise;
// Identify which calendar has changed
commonList = _.intersectionBy(newList, oldList, 'id');
ids = _.map(_.filter(commonList, function(o) {
var oldObject = _.find(oldList, { id: o.id });
return !_.isEqual(o, oldObject);
}), 'id');
promise = Calendar.$q.when();
// Identify which calendar has changed
commonList = _.intersectionBy(newList, oldList, 'id');
ids = _.map(_.filter(commonList, function(o) {
var oldObject = _.find(oldList, { id: o.id });
return !_.isEqual(o, oldObject);
}), 'id');
promise = Calendar.$q.when();
if (ids.length > 0) {
$log.debug(ids.join(', ') + ' changed');
promise = Calendar.saveFoldersActivation(ids);
}
if (ids.length > 0 || commonList.length != newList.length || commonList.length != oldList.length)
promise.then(function() {
$rootScope.$emit('calendars:list');
});
},
true // compare for object equality
);
if (ids.length > 0) {
$log.debug(ids.join(', ') + ' changed');
promise = Calendar.saveFoldersActivation(ids);
}
if (ids.length > 0 || commonList.length != newList.length || commonList.length != oldList.length)
promise.then(function() {
$rootScope.$emit('calendars:list');
});
},
true // compare for object equality
);
};
/**
* Only allow to sort items within the same list.

View File

@ -52,28 +52,26 @@
$refreshTimeout: null,
$ghost: {}
});
Preferences.ready().then(function() {
// Initialize filter parameters from user's settings
if (Preferences.settings.Calendar.EventsFilterState)
Component.$queryEvents.filterpopup = Preferences.settings.Calendar.EventsFilterState;
if (Preferences.settings.Calendar.TasksFilterState)
Component.$queryTasks.filterpopup = Preferences.settings.Calendar.TasksFilterState;
if (Preferences.settings.Calendar.EventsSortingState) {
Component.$queryEvents.sort = Preferences.settings.Calendar.EventsSortingState[0];
Component.$queryEvents.asc = parseInt(Preferences.settings.Calendar.EventsSortingState[1]);
}
if (Preferences.settings.Calendar.TasksSortingState) {
Component.$queryTasks.sort = Preferences.settings.Calendar.TasksSortingState[0];
Component.$queryTasks.asc = parseInt(Preferences.settings.Calendar.TasksSortingState[1]);
}
Component.$queryTasks.show_completed = parseInt(Preferences.settings.ShowCompletedTasks);
// Initialize categories from user's defaults
Component.$categories = Preferences.defaults.SOGoCalendarCategoriesColors;
// Initialize time format from user's defaults
if (Preferences.defaults.SOGoTimeFormat) {
Component.timeFormat = Preferences.defaults.SOGoTimeFormat;
}
});
// Initialize filter parameters from user's settings
if (Preferences.settings.Calendar.EventsFilterState)
Component.$queryEvents.filterpopup = Preferences.settings.Calendar.EventsFilterState;
if (Preferences.settings.Calendar.TasksFilterState)
Component.$queryTasks.filterpopup = Preferences.settings.Calendar.TasksFilterState;
if (Preferences.settings.Calendar.EventsSortingState) {
Component.$queryEvents.sort = Preferences.settings.Calendar.EventsSortingState[0];
Component.$queryEvents.asc = parseInt(Preferences.settings.Calendar.EventsSortingState[1]);
}
if (Preferences.settings.Calendar.TasksSortingState) {
Component.$queryTasks.sort = Preferences.settings.Calendar.TasksSortingState[0];
Component.$queryTasks.asc = parseInt(Preferences.settings.Calendar.TasksSortingState[1]);
}
Component.$queryTasks.show_completed = parseInt(Preferences.settings.ShowCompletedTasks);
// Initialize categories from user's defaults
Component.$categories = Preferences.defaults.SOGoCalendarCategoriesColors;
// Initialize time format from user's defaults
if (Preferences.defaults.SOGoTimeFormat) {
Component.timeFormat = Preferences.defaults.SOGoTimeFormat;
}
return Component; // return constructor
}];
@ -124,19 +122,15 @@
* current view.
*/
Component.$startRefreshTimeout = function(type) {
var _this = this;
if (Component.$refreshTimeout)
Component.$timeout.cancel(Component.$refreshTimeout);
Component.$Preferences.ready().then(function() {
// Restart the refresh timer, if needed
var refreshViewCheck = Component.$Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually') {
var f = angular.bind(Component.$rootScope, Component.$rootScope.$emit, 'calendars:list');
Component.$refreshTimeout = Component.$timeout(f, refreshViewCheck.timeInterval()*1000);
}
});
// Restart the refresh timer, if needed
var refreshViewCheck = Component.$Preferences.defaults.SOGoRefreshViewCheck;
if (refreshViewCheck && refreshViewCheck != 'manually') {
var f = angular.bind(Component.$rootScope, Component.$rootScope.$emit, 'calendars:list');
Component.$refreshTimeout = Component.$timeout(f, refreshViewCheck.timeInterval()*1000);
}
};
/**
@ -166,44 +160,41 @@
queryKey = '$query' + type.capitalize(),
params = {
day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day,
};
},
futureComponentData,
dirty = false,
otherType;
Component.$startRefreshTimeout(type);
return this.$Preferences.ready().then(function() {
var futureComponentData,
dirty = false,
otherType;
angular.extend(this.$query, params);
angular.extend(_this.$query, params);
if (options) {
_.forEach(_.keys(options), function(key) {
// Query parameters common to events and tasks are compared
dirty |= (_this.$query[key] && options[key] != Component.$query[key]);
if (key == 'reload' && options[key])
dirty = true;
// Update either the common parameters or the type-specific parameters
else if (angular.isDefined(_this.$query[key]))
_this.$query[key] = options[key];
else
_this[queryKey][key] = options[key];
});
}
if (options) {
_.forEach(_.keys(options), function(key) {
// Query parameters common to events and tasks are compared
dirty |= (_this.$query[key] && options[key] != Component.$query[key]);
if (key == 'reload' && options[key])
dirty = true;
// Update either the common parameters or the type-specific parameters
else if (angular.isDefined(_this.$query[key]))
_this.$query[key] = options[key];
else
_this[queryKey][key] = options[key];
});
}
// Perform query with both common and type-specific parameters
futureComponentData = this.$$resource.fetch(null, type + 'list',
angular.extend(this[queryKey], this.$query));
// Perform query with both common and type-specific parameters
futureComponentData = _this.$$resource.fetch(null, type + 'list',
angular.extend(_this[queryKey], _this.$query));
// Invalidate cached results of other type if $query has changed
if (dirty) {
otherType = (type == 'tasks')? '$events' : '$tasks';
delete Component[otherType];
Component.$log.debug('force reload of ' + otherType);
}
// Invalidate cached results of other type if $query has changed
if (dirty) {
otherType = (type == 'tasks')? '$events' : '$tasks';
delete Component[otherType];
Component.$log.debug('force reload of ' + otherType);
}
return _this.$unwrapCollection(type, futureComponentData);
});
return this.$unwrapCollection(type, futureComponentData);
};
/**
@ -256,40 +247,36 @@
* @returns a promise of a collection of objects describing the events blocks
*/
Component.$eventsBlocksForView = function(view, date) {
var _this = this;
var firstDayOfWeek, viewAction, startDate, endDate, params;
return Component.$Preferences.ready().then(function(data) {
var firstDayOfWeek, viewAction, startDate, endDate, params;
firstDayOfWeek = Component.$Preferences.defaults.SOGoFirstDayOfWeek;
if (view == 'day') {
viewAction = 'dayView';
startDate = endDate = date;
}
else if (view == 'multicolumnday') {
viewAction = 'multicolumndayView';
startDate = endDate = date;
}
else if (view == 'week') {
viewAction = 'weekView';
startDate = date.beginOfWeek(firstDayOfWeek);
endDate = new Date();
endDate.setTime(startDate.getTime());
endDate.addDays(6);
}
else if (view == 'month') {
viewAction = 'monthView';
startDate = date;
startDate.setDate(1);
startDate = startDate.beginOfWeek(firstDayOfWeek);
endDate = new Date();
endDate.setTime(date.getTime());
endDate.setMonth(endDate.getMonth() + 1);
endDate.addDays(-1);
endDate = endDate.endOfWeek(firstDayOfWeek);
}
return _this.$eventsBlocks(viewAction, startDate, endDate);
});
firstDayOfWeek = Component.$Preferences.defaults.SOGoFirstDayOfWeek;
if (view == 'day') {
viewAction = 'dayView';
startDate = endDate = date;
}
else if (view == 'multicolumnday') {
viewAction = 'multicolumndayView';
startDate = endDate = date;
}
else if (view == 'week') {
viewAction = 'weekView';
startDate = date.beginOfWeek(firstDayOfWeek);
endDate = new Date();
endDate.setTime(startDate.getTime());
endDate.addDays(6);
}
else if (view == 'month') {
viewAction = 'monthView';
startDate = date;
startDate.setDate(1);
startDate = startDate.beginOfWeek(firstDayOfWeek);
endDate = new Date();
endDate.setTime(date.getTime());
endDate.setMonth(endDate.getMonth() + 1);
endDate.addDays(-1);
endDate = endDate.endOfWeek(firstDayOfWeek);
}
return this.$eventsBlocks(viewAction, startDate, endDate);
};
/**
@ -533,12 +520,10 @@
if (this.c_category) {
// c_category is only defined in list mode (when calling $filter)
Component.$Preferences.ready().then(function() {
// Filter out categories for which there's no associated color
_this.categories = _.invokeMap(_.filter(_this.c_category, function(name) {
return Component.$Preferences.defaults.SOGoCalendarCategoriesColors[name];
}), 'asCSSIdentifier');
});
// Filter out categories for which there's no associated color
this.categories = _.invokeMap(_.filter(this.c_category, function(name) {
return Component.$Preferences.defaults.SOGoCalendarCategoriesColors[name];
}), 'asCSSIdentifier');
}
// Parse recurrence rule definition and initialize default values
@ -591,24 +576,22 @@
if (this.isNew) {
// Set default values
Component.$Preferences.ready().then(function() {
var type = (_this.type == 'appointment')? 'Events' : 'Tasks';
var type = (this.type == 'appointment')? 'Events' : 'Tasks';
// Set default classification
_this.classification = Component.$Preferences.defaults['SOGoCalendar' + type + 'DefaultClassification'].toLowerCase();
// Set default classification
this.classification = Component.$Preferences.defaults['SOGoCalendar' + type + 'DefaultClassification'].toLowerCase();
// Set default alarm
var units = { M: 'MINUTES', H: 'HOURS', D: 'DAYS', W: 'WEEKS' };
var match = /-PT?([0-9]+)([MHDW])/.exec(Component.$Preferences.defaults.SOGoCalendarDefaultReminder);
if (match) {
_this.$hasAlarm = true;
_this.alarm.quantity = parseInt(match[1]);
_this.alarm.unit = units[match[2]];
}
// Set default alarm
var units = { M: 'MINUTES', H: 'HOURS', D: 'DAYS', W: 'WEEKS' };
var match = /-PT?([0-9]+)([MHDW])/.exec(Component.$Preferences.defaults.SOGoCalendarDefaultReminder);
if (match) {
this.$hasAlarm = true;
this.alarm.quantity = parseInt(match[1]);
this.alarm.unit = units[match[2]];
}
// Set notitifications
_this.sendAppointmentNotifications = Component.$Preferences.defaults.SOGoAppointmentSendEMailNotifications;
});
// Set notitifications
this.sendAppointmentNotifications = Component.$Preferences.defaults.SOGoAppointmentSendEMailNotifications;
}
else if (angular.isUndefined(data.$hasAlarm)) {
this.$hasAlarm = angular.isDefined(data.alarm);

View File

@ -116,26 +116,24 @@
});
if ($location.url().length === 0) {
// Restore user's last view
Preferences.ready().then(function() {
var url = '/calendar/',
view = /(.+)view/.exec(Preferences.settings.Calendar.View);
if (view)
url += view[1];
else
url += 'week';
// Append today's date or next enabled weekday
var now = new Date();
if (Preferences.defaults.SOGoCalendarWeekdays) {
var weekDays = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
var weekDay = weekDays[now.getDay()];
while (Preferences.defaults.SOGoCalendarWeekdays.indexOf(weekDay) < 0) {
now.addDays(1);
weekDay = weekDays[now.getDay()];
}
var url = '/calendar/',
view = /(.+)view/.exec(Preferences.settings.Calendar.View);
if (view)
url += view[1];
else
url += 'week';
// Append today's date or next enabled weekday
var now = new Date();
if (Preferences.defaults.SOGoCalendarWeekdays) {
var weekDays = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
var weekDay = weekDays[now.getDay()];
while (Preferences.defaults.SOGoCalendarWeekdays.indexOf(weekDay) < 0) {
now.addDays(1);
weekDay = weekDays[now.getDay()];
}
url += '/' + now.getDayString();
$location.replace().url(url);
});
}
url += '/' + now.getDayString();
$location.replace().url(url);
}
}

View File

@ -48,17 +48,16 @@
function initView() {
view = new sgScrollView(element, type);
if (type != 'monthly')
if (type != 'monthly') {
// Scroll to the day start hour defined in the user's defaults
Preferences.ready().then(function() {
var time, hourCell, quartersOffset;
if (Preferences.defaults.SOGoDayStartTime) {
time = Preferences.defaults.SOGoDayStartTime.split(':');
hourCell = document.getElementById('hour' + parseInt(time[0]));
quartersOffset = parseInt(time[1]) * view.quarterHeight;
view.element.scrollTop = hourCell.offsetTop + quartersOffset;
}
});
var time, hourCell, quartersOffset;
if (Preferences.defaults.SOGoDayStartTime) {
time = Preferences.defaults.SOGoDayStartTime.split(':');
hourCell = document.getElementById('hour' + parseInt(time[0]));
quartersOffset = parseInt(time[1]) * view.quarterHeight;
view.element.scrollTop = hourCell.offsetTop + quartersOffset;
}
}
// Expose quarter height to the controller
// See sgNowLine directive