diff --git a/CHANGES b/CHANGES
index e590ba0f6..6a8590856 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2730,4 +2730,5 @@
* ldap: fix error messages to show proper error messages in the external LDAP connector
* dashboard: fix various UI elements hidden for admin user
* directoryserver: fix totp validation
+* email: improve loading of the mail usage to not block other views from loading
diff --git a/dashboard/src/views/emails.html b/dashboard/src/views/emails.html
index bd111598d..637528acb 100644
--- a/dashboard/src/views/emails.html
+++ b/dashboard/src/views/emails.html
@@ -195,10 +195,20 @@
- {{ 'main.loadingPlaceholder' | tr }} ...
- {{ 'emails.domains.stats' | tr:{ mailboxCount: domain.mailboxCount, usage: (domain.usage | prettyDecimalSize) } }}
- {{ 'emails.domains.outbound' | tr }}
- {{ 'emails.domains.disabled' | tr }}
+
+ {{ 'main.loadingPlaceholder' | tr }} ...
+
+
+
+ {{ 'emails.domains.stats' | tr:{ mailboxCount: domain.mailboxCount } }} {{ 'main.loadingPlaceholder' | tr }} ...
+ {{ 'emails.domains.stats' | tr:{ mailboxCount: domain.mailboxCount, usage: (domain.usage | prettyDecimalSize) } }}
+
+
+ {{ 'emails.domains.outbound' | tr }}
+ {{ 'emails.domains.disabled' | tr }}
+
+
+
|
diff --git a/dashboard/src/views/emails.js b/dashboard/src/views/emails.js
index ff507efb5..f9238463c 100644
--- a/dashboard/src/views/emails.js
+++ b/dashboard/src/views/emails.js
@@ -1,6 +1,7 @@
'use strict';
/* global $, angular, TASK_TYPES */
+/* global async */
angular.module('Application').controller('EmailsController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) {
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastMailManager) $location.path('/'); });
@@ -404,44 +405,83 @@ angular.module('Application').controller('EmailsController', ['$scope', '$locati
}
};
- function refreshDomainStatuses() {
- $scope.domains.forEach(function (domain) {
- domain.usage = null; // used by ui to show 'loading'
+ function refreshMailStatus(domain, done) {
+ Client.getMailStatusForDomain(domain.domain, function (error, result) {
+ if (error) {
+ console.error('Failed to fetch mail status for domain', domain.domain, error);
+ return done();
+ }
- Client.getMailStatusForDomain(domain.domain, function (error, result) {
- if (error) return console.error('Failed to fetch mail status for domain', domain.domain, error);
+ domain.status = result;
- domain.status = result;
+ domain.statusOk = Object.keys(result).every(function (k) {
+ if (k === 'dns') return Object.keys(result.dns).every(function (k) { return result.dns[k].status; });
- domain.statusOk = Object.keys(result).every(function (k) {
- if (k === 'dns') return Object.keys(result.dns).every(function (k) { return result.dns[k].status; });
+ if (!('status' in result[k])) return true; // if status is not present, the test was not run
- if (!('status' in result[k])) return true; // if status is not present, the test was not run
-
- return result[k].status;
- });
+ return result[k].status;
});
- Client.getMailConfigForDomain(domain.domain, function (error, mailConfig) {
- if (error) return console.error('Failed to fetch mail config for domain', domain.domain, error);
+ done();
+ });
+ }
- domain.inbound = mailConfig.enabled;
- domain.outbound = mailConfig.relay.provider !== 'noop';
+ function refreshMailConfig(domain, done) {
+ Client.getMailConfigForDomain(domain.domain, function (error, mailConfig) {
+ if (error) {
+ console.error('Failed to fetch mail config for domain', domain.domain, error);
+ return done();
+ }
- // do this even if no outbound since people forget to remove mailboxes
- Client.getMailboxCount(domain.domain, function (error, count) {
- if (error) return console.error('Failed to fetch mailboxes for domain', domain.domain, error);
+ domain.inbound = mailConfig.enabled;
+ domain.outbound = mailConfig.relay.provider !== 'noop';
- domain.mailboxCount = count;
+ // do this even if no outbound since people forget to remove mailboxes
+ Client.getMailboxCount(domain.domain, function (error, count) {
+ if (error) {
+ console.error('Failed to fetch mailboxes for domain', domain.domain, error);
+ return done();
+ }
- Client.getMailUsage(domain.domain, function (error, usage) {
- if (error) return console.error('Failed to fetch usage for domain', domain.domain, error);
+ domain.mailboxCount = count;
- domain.usage = 0;
- // we used to use quotaValue here but it's quite different wrt diskSize. so choose diskSize consistently
- // also, quotaValue can be missing for deleted mailboxes that are on disk but removed from dovecot/ldap itself
- Object.keys(usage).forEach(function (m) { domain.usage += usage[m].diskSize; });
- });
+ done();
+ });
+ });
+ }
+
+ function refreshMailUsage(domain, done) {
+ Client.getMailUsage(domain.domain, function (error, usage) {
+ if (error) {
+ console.error('Failed to fetch usage for domain', domain.domain, error);
+ return done();
+ }
+
+ domain.usage = 0;
+ // we used to use quotaValue here but it's quite different wrt diskSize. so choose diskSize consistently
+ // also, quotaValue can be missing for deleted mailboxes that are on disk but removed from dovecot/ldap itself
+ Object.keys(usage).forEach(function (m) { domain.usage += usage[m].diskSize; });
+
+ done();
+ });
+ }
+
+ function refreshDomainStatuses() {
+ async.each($scope.domains, function (domain, iteratorDone) {
+ async.series([
+ refreshMailStatus.bind(null, domain),
+ refreshMailConfig.bind(null, domain),
+ ], function () {
+ domain.loading = false;
+ iteratorDone();
+ });
+ }, function () {
+ // mail usage is loaded separately with a cancellation check. when there are a lot of domains, it runs a long time in background and slows down loading of new views
+ async.eachLimit($scope.domains, 5, function (domain, itemDone) {
+ if ($scope.$$destroyed) return itemDone(); // abort!
+ refreshMailUsage(domain, function () {
+ domain.loadingUsage = false;
+ itemDone();
});
});
});
@@ -451,7 +491,9 @@ angular.module('Application').controller('EmailsController', ['$scope', '$locati
Client.getDomains(function (error, domains) {
if (error) return console.error('Unable to get domain listing.', error);
+ domains.forEach(function (domain) { domain.loading = true; domain.loadingUsage = true; }); // used by ui to show 'loading'
$scope.domains = domains;
+
$scope.ready = true;
if ($scope.user.isAtLeastAdmin) {
|