diff --git a/avAdmin/admin-directives/create/create.js b/avAdmin/admin-directives/create/create.js
index 83a133f4..7121baeb 100644
--- a/avAdmin/admin-directives/create/create.js
+++ b/avAdmin/admin-directives/create/create.js
@@ -1044,6 +1044,48 @@ angular.module('avAdmin')
}
});
+ function checkTrustees(el) {
+ logInfo($i18next.t('avAdmin.create.checkingTrustees', {title: el.title}));
+ var deferred = $q.defer();
+ var auths = el.authorities && Array.from(el.authorities) || [];
+ if (el.director) {
+ auths.push(el.director);
+ }
+
+ if (0 === auths.length) {
+ logError($i18next.t('avAdmin.create.errors.election-auths-missing', {eltitle: el.title}));
+ deferred.reject();
+ } else {
+ ElectionsApi
+ .authoritiesStatus()
+ .then(function (trustees) {
+ var hasError = false;
+ for (var i = 0; i < auths.length; i++) {
+ var auth = auths[i];
+ if (!trustees[auth]) {
+ logError($i18next.t('avAdmin.create.errors.election-auth-not-found', {eltitle: el.title, auth: auth}));
+ hasError = true;
+ continue;
+ }
+ if ('ok' !== trustees[auth].state) {
+ logError($i18next.t('avAdmin.create.errors.election-auth-error', {eltitle: el.title, auth: auth, message: trustees[auth].message}));
+ hasError = true;
+ continue;
+ }
+ }
+ if (hasError) {
+ deferred.reject();
+ } else {
+ deferred.resolve(el);
+ }
+ })
+ .catch(deferred.reject);
+ }
+
+
+ return deferred.promise;
+ }
+
function createAuthEvent(el) {
console.log("creating auth event for election " + el.title);
var deferred = $q.defer();
@@ -1264,6 +1306,7 @@ angular.module('avAdmin')
var promise = deferred.promise;
promise = promise
+ .then(checkTrustees)
.then(createAuthEvent)
.then(registerElection)
.then(function(election) {
diff --git a/avAdmin/admin-directives/dashboard/dashboard.html b/avAdmin/admin-directives/dashboard/dashboard.html
index 2222fbac..a8a82e90 100644
--- a/avAdmin/admin-directives/dashboard/dashboard.html
+++ b/avAdmin/admin-directives/dashboard/dashboard.html
@@ -212,8 +212,14 @@
-
-
- {{ auth }}
+ {{ auth }}
+
+
+ {{ getTrusteeMsg(auth) }}
+
+
+
+
diff --git a/avAdmin/admin-directives/dashboard/dashboard.js b/avAdmin/admin-directives/dashboard/dashboard.js
index aa4ac26e..3cbe9074 100644
--- a/avAdmin/admin-directives/dashboard/dashboard.js
+++ b/avAdmin/admin-directives/dashboard/dashboard.js
@@ -22,6 +22,7 @@ angular.module('avAdmin')
$q,
$window,
$state,
+ $i18next,
Authmethod,
Plugins,
ElectionsApi,
@@ -37,6 +38,7 @@ angular.module('avAdmin')
function link(scope, element, attrs)
{
scope.reloadTimeout = null;
+ scope.trusteesState = {};
scope.isWriteInResult = function(answer)
{
@@ -81,8 +83,25 @@ angular.module('avAdmin')
}
}
+ function setTrusteesState() {
+ ElectionsApi
+ .authoritiesStatus()
+ .then(function (trustees) {
+ scope.trusteesState = trustees;
+ });
+ }
+
+ function isTrusteeOk(name) {
+ return scope.trusteesState && scope.trusteesState[name] && 'ok' === scope.trusteesState[name].state;
+ }
+
+ function getTrusteeMsg(name) {
+ return scope.trusteesState && scope.trusteesState[name] && scope.trusteesState && scope.trusteesState[name].message || '';
+ }
+
function waitElectionChange()
{
+ setTrusteesState();
ElectionsApi
.getElection(scope.id, /*ignorecache = */ true)
.then(function(el)
@@ -743,6 +762,46 @@ angular.module('avAdmin')
);
}
+ function checkTrustees() {
+ var deferred = $q.defer();
+
+ var errors = [];
+
+ var auths = scope.election.authorities && Array.from(scope.election.authorities) || [];
+ if (scope.election.director) {
+ auths.push(scope.election.director);
+ }
+
+ if (0 === auths.length) {
+ errors.push($i18next.t('avAdmin.create.errors.election-auths-missing', {eltitle: scope.election.title}));
+ deferred.reject(errors.join("\n"));
+ } else {
+ ElectionsApi
+ .authoritiesStatus()
+ .then(function (trustees) {
+ for (var i = 0; i < auths.length; i++) {
+ var auth = auths[i];
+ if (!trustees[auth]) {
+ errors.push($i18next.t('avAdmin.create.errors.election-auth-not-found', {eltitle: scope.election.title, auth: auth}));
+ continue;
+ }
+ if ('ok' !== trustees[auth].state) {
+ errors.push($i18next.t('avAdmin.create.errors.election-auth-error', {eltitle: scope.election.title, auth: auth, message: trustees[auth].message}));
+ continue;
+ }
+ }
+ if (errors.length > 0) {
+ deferred.reject(errors);
+ } else {
+ deferred.resolve();
+ }
+ })
+ .catch(deferred.reject);
+ }
+
+ return deferred.promise;
+ }
+
// performs all the initialization
function init()
{
@@ -895,13 +954,17 @@ angular.module('avAdmin')
scope.index = scope.getStatusIndex('stopped') + 1;
}
scope.nextaction = false;
- Authmethod
- .launchTally(
- scope.election.id,
- data.tallyElectionIds,
- 'force-all',
- data.mode
- )
+
+ checkTrustees()
+ .then(function () {
+ return Authmethod
+ .launchTally(
+ scope.election.id,
+ data.tallyElectionIds,
+ 'force-all',
+ data.mode
+ );
+ })
.then(
function onSuccess()
{
@@ -915,7 +978,15 @@ angular.module('avAdmin')
scope.loading = false;
scope.error = error;
}
- );
+ )
+ .catch(function(error)
+ {
+ if (scope.launchedTally) {
+ scope.launchedTally = false;
+ }
+ scope.loading = false;
+ scope.error = error;
+ });
},
enableFunc: function () {
return (
@@ -1460,6 +1531,7 @@ angular.module('avAdmin')
scope.prevStatus = null;
scope.percentVotes = PercentVotesService;
+ setTrusteesState();
// get the election at the begining
ElectionsApi
.getElection(scope.id)
@@ -1532,6 +1604,8 @@ angular.module('avAdmin')
launchKeyDistributionCeremony: launchKeyDistributionCeremony,
launchOpeningCeremony: launchOpeningCeremony,
configureScheduledEvents: configureScheduledEvents,
+ isTrusteeOk: isTrusteeOk,
+ getTrusteeMsg: getTrusteeMsg,
downloadTurnout: downloadTurnout
});
diff --git a/avAdmin/admin-directives/dashboard/dashboard.less b/avAdmin/admin-directives/dashboard/dashboard.less
index 86697b69..ad90c00b 100644
--- a/avAdmin/admin-directives/dashboard/dashboard.less
+++ b/avAdmin/admin-directives/dashboard/dashboard.less
@@ -21,6 +21,43 @@
}
}
+.authority {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.auth-badge {
+ width: 25px;
+ height: 25px;
+ border-radius: 25px;
+ background-color: @error-primary-color;
+ margin: auto 0;
+ position: relative;
+
+ &.auth-badge-green {
+ background-color: @av-secondary-contrast;
+ }
+
+ .auth-tooltip {
+ visibility: hidden;
+ width: 240px;
+ background-color: @av-bg;
+ text-align: center;
+ padding: 5px 0;
+ border-radius: 6px;
+ position: absolute;
+ z-index: 1;
+ font-size: 1rem;
+ top: 25px;
+ left: 16px;
+ }
+}
+
+.auth-badge:hover .auth-tooltip {
+ visibility: visible;
+}
+
/** descriptive radio buttons */
.descriptive-radio {
border: 2px solid transparent;
diff --git a/avAdmin/elections-api-service.js b/avAdmin/elections-api-service.js
index 513ef653..6e772289 100644
--- a/avAdmin/elections-api-service.js
+++ b/avAdmin/elections-api-service.js
@@ -275,6 +275,25 @@ angular.module('avAdmin')
return deferred.promise;
};
+ electionsapi.authoritiesStatus = function ()
+ {
+ var deferred = $q.defer();$http
+ .get(
+ backendUrl + 'authorities-state'
+ )
+ .then(
+ function (response) {
+ if (response && response.data && response.data.payload) {
+ deferred.resolve(response.data.payload);
+ } else {
+ deferred.reject(response);
+ }
+ },
+ deferred.reject
+ );
+ return deferred.promise;
+ };
+
electionsapi.parseElection = function(d) {
var election = d.payload;
var conf = electionsapi.templateEl();
diff --git a/locales/en.json b/locales/en.json
index 909f3a15..90a23f28 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -934,6 +934,7 @@
"create": {
"summary": "Summary: creating __num__ elections",
"summary__plural": "Summary: creating __num__ elections",
+ "checkingTrustees": "Checking trustees status for election __title__",
"create": "Create the elections",
"creating": "Creating the authentication for election __title__",
"setChildrenElectionInfo": "Setting the children election info for election (id: __id__) __title__",
@@ -954,6 +955,9 @@
"confirmEdit": "Finish edit"
},
"errors": {
+ "election-auths-missing": "Election '__eltitle__': Missing trustees",
+ "election-auth-not-found": "Election '__eltitle__': Trustee not found '__auth__'",
+ "election-auth-error": "Election '__eltitle__': Trustee '__auth__' has invalid status '__message__'",
"lambda-live-preview-parent-children": "Missing election IDs for a parent-children election",
"election-is-array-questions": "Election '__eltitle__': list of questions is not an array",
"election-lambda-success-action-url-mode": "Election '__eltitle__': invalid success action url",
diff --git a/locales/es.json b/locales/es.json
index fca96d12..0e251fc5 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -821,6 +821,7 @@
"create": {
"summary": "Resumen: creando __num__ votaciones",
"summary__plural": "Resumen: creando __num__ votaciones",
+ "checkingTrustees": "Checking trustees status for election __title__",
"create": "Crear las votaciones",
"create__plural": "Crear las votaciones",
"creating": "Creando la autenticación de la votación __title__",
@@ -834,6 +835,9 @@
"livePreview": "Previsualización en vivo"
},
"errors": {
+ "election-auths-missing": "Election '__eltitle__': Faltan autoridades",
+ "election-auth-not-found": "Election '__eltitle__': Autoridad no encontrada '__auth__'",
+ "election-auth-error": "Election '__eltitle__': Autoridad '__auth__' tiene un estado inválido '__message__'",
"lambda-live-preview-parent-children": "Faltan ID de la votación para una votación padre-hijos",
"election-is-array-questions": "Votación '__eltitle__': la lista de pregunta no es un array",
"election-lambda-success-action-url-mode": "Votación '__eltitle__': url de redirección en 'Acción de éxito' inválida",