diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json index 59d645d193544a65156663d05a4bdd020b78f697..07c762c952021e31d7e8c444b28c8148e183c7b9 100644 --- a/www/i18n/locale-en.json +++ b/www/i18n/locale-en.json @@ -305,8 +305,8 @@ "SALT_NOT_CONFIRMED": "Must match previous phrase.", "SEND_IDENTITY_FAILED": "Error while trying to register.", "SEND_CERTIFICATION_FAILED": "Could not certify identity.", - "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "You could not send certification, because your account is <b>not</b> a member account.", - "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "You could not send certification now, because your are a member yet.<br/>Be patient ! ;-)", + "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "You could not send certification, because your account is <b>not a member account</b>.", + "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "You could not send certification now, because your are <b>not a member</b> yet.<br/><br/>You still need certification to become a member.", "NOT_MEMBER_FOR_CERTIFICATION": "Your account is not a member account yet.", "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "This account could not be certified. No registration found, or need to renew.", "LOGIN_FAILED": "Error while sign in.", diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json index 707dcc93728f18703531f9f0021f1fa97c56c575..728b44f53d93e674433f5cb62c576c68b92b3f29 100644 --- a/www/i18n/locale-fr-FR.json +++ b/www/i18n/locale-fr-FR.json @@ -306,7 +306,7 @@ "SEND_IDENTITY_FAILED": "Echec de l'inscription.", "SEND_CERTIFICATION_FAILED": "Echec de la certification.", "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "Vous ne pouvez pas effectuer de certification, car votre compte n'est <b>pas membre</b>.", - "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "Vous ne pouvez pas certifier quelqu'un, car vous n'êtes pas encore devenu membre.<br/>Patience ! ;-)", + "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "Vous ne pouvez pas effectuer de certification, car votre compte n'est <p>pas encore membre</b>.<br/><br/>Il vous manque encore des certification pour devenir membre.", "NOT_MEMBER_FOR_CERTIFICATION": "Votre compte n'est pas encore membre.", "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "Compte non certifiable. Aucune demande d'adhésion n'a été faite, ou bien elle n'a pas été renouvellée.", "LOGIN_FAILED": "Erreur lors de l'authentification.", diff --git a/www/js/config.js b/www/js/config.js index f9ebe07493171e815ff94ef620a5dfe997910972..72c97a39affb919946e7b6962a8d5882ed251d6e 100644 --- a/www/js/config.js +++ b/www/js/config.js @@ -10,39 +10,35 @@ angular.module("cesium.config", []) .constant("csConfig", { "cacheTimeMs": 60000, - "fallbackLanguage": "fr-FR", - "defaultLanguage": "fr-FR", - "rememberMe": true, + "fallbackLanguage": "en", + "rememberMe": false, "showUDHistory": false, - "timeout": 6000, + "timeout": 10000, "timeWarningExpireMembership": 5184000, "timeWarningExpire": 7776000, "useLocalStorage": true, "useRelative": true, "initPhase": false, "expertMode": false, - "decimalCount": 2, + "decimalCount": 4, "helptip": { "enable": true, - "installDocUrl": { - "fr-FR": "http://www.le-sou.org/devenir-noeud/", - "en": "https://github.com/duniter/duniter/blob/master/doc/install-a-node.md" - } + "installDocUrl": "https://github.com/duniter/duniter/blob/master/doc/install-a-node.md" }, "node": { - "host": "duniter.le-sou.org", - "port": "9600" + "host": "cgeek.fr", + "port": "9330" }, "plugins": { "es": { "enable": true, - "askEnable": true, - "host": "data.le-sou.org", + "askEnable": false, + "host": "data.duniter.fr", "port": "80" } }, "version": "0.5.0", - "build": "2016-11-09T20:11:53.167Z", + "build": "2016-11-12T10:57:58.316Z", "newIssueUrl": "https://github.com/duniter/cesium/issues/new?labels=bug" }) diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js index c0dbc9ccd7b2c6948bf2578727cb2c9b45df6c1f..7f8ca0a9981a06219b39c912bc98713f0f1a3c63 100644 --- a/www/js/controllers/wot-controllers.js +++ b/www/js/controllers/wot-controllers.js @@ -455,7 +455,10 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ $scope.formData = identity; $scope.canCertify = $scope.formData.hasSelf && (!csWallet.isLogin() || (!csWallet.isUserPubkey(pubkey))); $scope.canSelectAndCertify = $scope.formData.hasSelf && csWallet.isUserPubkey(pubkey); - $scope.alreadyCertified = $scope.canCertify ? !!_.findWhere(identity.received_cert, { uid: csWallet.data.uid, valid: true }) : false; + $scope.alreadyCertified = $scope.canCertify ? + (!csWallet.isLogin() || + !!_.findWhere(identity.received_cert, { pubkey: csWallet.data.pubkey, valid: true }) || + !!_.findWhere(identity.received_cert_pending, { pubkey: csWallet.data.pubkey, valid: true })) : false; $scope.loading = false; @@ -474,12 +477,34 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ $scope.certify = function() { $scope.loadWallet() .then(function() { + UIUtils.loading.hide(); + if (!csConfig.initPhase && !$rootScope.walletData.isMember) { UIUtils.alert.error($rootScope.walletData.requirements.needSelf ? 'ERROR.NEED_MEMBER_ACCOUNT_TO_CERTIFY' : 'ERROR.NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF'); return; } - UIUtils.loading.hide(); + + // Check not already certified + var previousCert = _.findWhere($scope.formData.received_cert, { pubkey: csWallet.data.pubkey, valid: true}); + if (previousCert) { + $translate('ERROR.IDENTITY_ALREADY_CERTIFY', previousCert) + .then(function(message) { + UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE'); + }); + return; + } + + // Check not pending certification + previousCert = _.findWhere($scope.formData.received_cert_pending, { pubkey: csWallet.data.pubkey, valid: true}); + if (previousCert) { + $translate('ERROR.IDENTITY_ALREADY_CERTIFY_PENDING', previousCert) + .then(function(message) { + UIUtils.alert.error(message, 'ERROR.UNABLE_TO_CERTIFY_TITLE'); + }); + return; + } + UIUtils.alert.confirm('CONFIRM.CERTIFY_RULES') .then(function(confirm){ if (!confirm) { @@ -489,10 +514,19 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ csWallet.certify($scope.formData.uid, $scope.formData.pubkey, $scope.formData.timestamp, - $scope.formData.sig) - .then(function() { + $scope.formData.sig, + $scope.formData.isMember, + $scope.formData.wasMember) + .then(function(cert) { UIUtils.loading.hide(); - UIUtils.alert.info('INFO.CERTIFICATION_DONE'); + if (cert) { + cert.uid = csWallet.data.uid; + cert.pubkey = csWallet.data.pubkey; + cert.isMember = csWallet.data.isMember; + UIUtils.alert.info('INFO.CERTIFICATION_DONE'); + $scope.formData.received_cert_pending.unshift(cert); + $scope.motionCertifications(); + } }) .catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED')); }); @@ -506,7 +540,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ .catch(UIUtils.onError('ERROR.LOGIN_FAILED')) .then(function() { if (!csConfig.initPhase && !$rootScope.walletData.isMember) { - UIUtils.alert.error($rootScope.walletData.requirements.needSelf ? + UIUtils.alert.error($rootScope.walletData.requirements.needSelf || $rootScope.walletData.requirements.needMembership ? 'ERROR.NEED_MEMBER_ACCOUNT_TO_CERTIFY' : 'ERROR.NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF'); return; } @@ -536,7 +570,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ } // Check not already certified - var previousCert = _.findWhere(identity.certifications, { uid: csWallet.data.uid, pending: false, valid: true}); + var previousCert = _.findWhere(identity.received_cert, { pubkey: csWallet.data.pubkey, valid: true}); if (previousCert) { $translate('ERROR.IDENTITY_ALREADY_CERTIFY', previousCert) .then(function(message) { @@ -546,7 +580,7 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ } // Check not pending certification - previousCert = _.findWhere(identity.certifications, { uid: csWallet.data.uid, pending: true, valid: true}); + previousCert = _.findWhere(identity.received_cert_pending, { pubkey: csWallet.data.pubkey, valid: true}); if (previousCert) { $translate('ERROR.IDENTITY_ALREADY_CERTIFY_PENDING', previousCert) .then(function(message) { @@ -570,10 +604,16 @@ function WotCertificationsViewController($scope, $rootScope, $state, $timeout, $ csWallet.certify(identity.uid, identity.pubkey, identity.timestamp, - identity.sig) - .then(function() { + identity.sig, + identity.isMember, + identity.wasMember) + .then(function(cert) { UIUtils.loading.hide(); - UIUtils.alert.info('INFO.CERTIFICATION_DONE'); + if (cert) { + UIUtils.alert.info('INFO.CERTIFICATION_DONE'); + $scope.formData.given_cert_pending.unshift(cert); + $scope.motionGivenCertifications(); + } }) .catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED')); }); diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js index 17d2277354527180776a39b827d5c28c281f5bc4..17052bf29c5b105ee7b30e2e0eebaf34ddc3f76c 100644 --- a/www/js/services/wallet-services.js +++ b/www/js/services/wallet-services.js @@ -196,6 +196,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser isNeverUsed = function() { return !data.pubkey || (!data.isMember && + (!data.requirements || !data.requirements.pendingMembership) && !data.tx.history.length && !data.tx.pendings.length); }, @@ -329,7 +330,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser data.blockUid = idty.meta.timestamp; // Add useful custom fields data.requirements.needSelf = false; - data.requirements.needMembership = (data.requirements.membershipExpiresIn === 0 && + data.requirements.needMembership = (data.requirements.membershipExpiresIn <= 0 && data.requirements.membershipPendingExpiresIn <= 0 ); data.requirements.needRenew = (!data.requirements.needMembership && data.requirements.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership && @@ -343,7 +344,7 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser } return count; }, 0) : 0; - data.isMember = !data.requirements.needSelf && !data.requirements.needMembership; + data.isMember = (data.requirements.membershipExpiresIn > 0); var blockParts = idty.meta.timestamp.split('-', 2); var blockNumber = parseInt(blockParts[0]); @@ -354,9 +355,9 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser data.sigDate = block.time; // Check if self has been done on a valid block - if (!data.isMember && blockNumber!== 0 && blockHash !== block.hash) { + if (!data.isMember && blockNumber !== 0 && blockHash !== block.hash) { addEvent({type: 'error', message: 'ERROR.WALLET_INVALID_BLOCK_HASH'}); - console.debug("Invalid membership for uid={0}: block hash not match a real block (block cancelled)".format(data.uid)); + console.debug("Invalid membership for uid={0}: block hash changed".format(data.uid)); } resolve(); }) @@ -739,12 +740,16 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser enable: true, fromTime: data.tx ? data.tx.fromTime : undefined // keep previous time }, - sigStock: true + sigStock: true, + api: true }; } var jobs = []; + // Reset events + data.events = []; + // Get current UD if (options.currentUd) jobs.push(loadCurrentUD()); @@ -1223,85 +1228,91 @@ angular.module('cesium.wallet.services', ['ngResource', 'ngApi', 'cesium.bma.ser */ membership = function(sideIn) { return function() { - return $q(function(resolve, reject) { - BMA.blockchain.current() + var membership; + return BMA.blockchain.current() .catch(function(err){ // Special case for currency init (root block not exists): use fixed values if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) { return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH}; } - else { - throw err; - } + throw err; }) .then(function(block) { // Create membership to sign - var membership = 'Version: 2\n' + - 'Type: Membership\n' + - 'Currency: ' + data.currency + '\n' + - 'Issuer: ' + data.pubkey + '\n' + - 'Block: ' + block.number + '-' + block.hash + '\n' + - 'Membership: ' + (!!sideIn ? "IN" : "OUT" ) + '\n' + - 'UserID: ' + data.uid + '\n' + - 'CertTS: ' + data.blockUid + '\n'; - - CryptoUtils.sign(membership, data.keypair) - .then(function(signature) { - var signedMembership = membership + signature + '\n'; - // Send signed membership - BMA.blockchain.membership({membership: signedMembership}) - .then(function(result) { - $timeout(function() { - loadRequirements() - .then(function() { - finishLoadRequirements(); - resolve(); - }) - .catch(function(err){reject(err);}); - }, 1000); // waiting for node to process membership doc - }).catch(function(err){reject(err);}); - }).catch(function(err){reject(err);}); - }).catch(function(err){reject(err);}); - }); + membership = 'Version: 2\n' + + 'Type: Membership\n' + + 'Currency: ' + data.currency + '\n' + + 'Issuer: ' + data.pubkey + '\n' + + 'Block: ' + block.number + '-' + block.hash + '\n' + + 'Membership: ' + (!!sideIn ? "IN" : "OUT" ) + '\n' + + 'UserID: ' + data.uid + '\n' + + 'CertTS: ' + data.blockUid + '\n'; + + return CryptoUtils.sign(membership, data.keypair); + }) + .then(function(signature) { + var signedMembership = membership + signature + '\n'; + // Send signed membership + return BMA.blockchain.membership({membership: signedMembership}); + }) + .then(function() { + return $timeout(function() { + return loadRequirements(); + }, 1000); // waiting for node to process membership doc + }) + .then(function() { + finishLoadRequirements(); + }); }; }, /** * Send identity certification */ - certify = function(uid, pubkey, timestamp, signature) { - return $q(function(resolve, reject) { + certify = function(uid, pubkey, timestamp, signature, isMember, wasMember) { + var current; + var cert; - BMA.blockchain.current() + return BMA.blockchain.current() .catch(function(err){ // Special case for currency init (root block not exists): use fixed values if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) { - return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH}; + return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH, medianTime: Math.trunc(new Date().getTime() / 1000)}; } - reject(err); + throw err; }) .then(function(block) { + current = block; // Create the self part to sign - var cert = 'Version: 2\n' + - 'Type: Certification\n' + - 'Currency: ' + data.currency + '\n' + - 'Issuer: ' + data.pubkey + '\n' + - 'IdtyIssuer: '+ pubkey + '\n' + - 'IdtyUniqueID: '+ uid + '\n' + - 'IdtyTimestamp: '+ timestamp + '\n' + - 'IdtySignature: '+ signature + '\n' + - 'CertTimestamp: '+ block.number + '-' + block.hash + '\n'; - - CryptoUtils.sign(cert, data.keypair) - .then(function(signature) { - var signedCert = cert + signature + '\n'; - BMA.wot.certify({cert: signedCert}) - .then(function(result) { - resolve(result); - }).catch(function(err){reject(err);}); - }).catch(function(err){reject(err);}); - }).catch(function(err){reject(err);}); - }); + cert = 'Version: 2\n' + + 'Type: Certification\n' + + 'Currency: ' + data.currency + '\n' + + 'Issuer: ' + data.pubkey + '\n' + + 'IdtyIssuer: ' + pubkey + '\n' + + 'IdtyUniqueID: ' + uid + '\n' + + 'IdtyTimestamp: ' + timestamp + '\n' + + 'IdtySignature: ' + signature + '\n' + + 'CertTimestamp: ' + block.number + '-' + block.hash + '\n'; + + return CryptoUtils.sign(cert, data.keypair); + }) + .then(function(signature) { + var signedCert = cert + signature + '\n'; + return BMA.wot.certify({cert: signedCert}); + }) + .then(function() { + return { + pubkey: pubkey, + uid: uid, + time: current.medianTime, + isMember: isMember, + wasMember: wasMember, + expiresIn: data.parameters.sigWindow, + pending: true, + block: current.number, + valid: true + }; + }); }, addEvent = function(event) { diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js index 79fc224573df6473c6553b6a1f076219812dc902..6cc428e5580f2a02f0326ac29e3f82df647ec5e7 100644 --- a/www/js/services/wot-services.js +++ b/www/js/services/wot-services.js @@ -83,8 +83,6 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic canMembershipOut: false, needRenew: false, pendingMembership: false, - certificationCount: 0, - certifications: [], needCertifications: false, needCertificationCount: 0, willNeedCertificationCount: 0 @@ -684,7 +682,7 @@ angular.module('cesium.wot.services', ['ngResource', 'ngApi', 'cesium.bma.servic idty.sigDate = block.medianTime; if (block.number !== 0 && idty.blockHash !== block.hash) { addEvent(idty, {type:'error', message: 'ERROR.WOT_PENDING_INVALID_BLOCK_HASH'}); - console.debug("Invalid membership for uid={0}: block hash not match a real block (block cancelled)".format(idty.uid)); + console.debug("Invalid membership for uid={0}: block hash changed".format(idty.uid)); } }); }); diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html index f55832356cd721fd138f59305b6d53d3bdbdfc84..d4e2eaa50f6750192cc3d781074ebd2d711cd28f 100644 --- a/www/templates/wallet/view_wallet.html +++ b/www/templates/wallet/view_wallet.html @@ -24,9 +24,8 @@ ng-style="walletData.avatarStyle" ng-class="{'avatar-wallet': !walletData.avatarStyle && !walletData.isMember, 'avatar-member': !walletData.avatarStyle && walletData.isMember}"> </i> - <h3 class="light" ng-if="walletData.name">{{walletData.name}}</h3> - <h3 class="light" ng-if="!walletData.name && walletData.isMember">{{walletData.uid}}</h3> - <h3 class="light" ng-if="!walletData.name && !walletData.isMember"><i class="icon ion-key"></i> {{walletData.pubkey | formatPubkey}}</h3> + <h3 class="light" ng-if="walletData.name || walletData.uid">{{walletData.name || walletData.uid}}</h3> + <h3 class="light" ng-if="!walletData.name && !walletData.uid"><i class="icon ion-key"></i> {{walletData.pubkey | formatPubkey}}</h3> <h4 class="light"> <span ng-if="!$root.settings.useRelative">{{convertedBalance | formatInteger}} {{walletData.parameters.currency | abbreviate}}</span> <span ng-if="$root.settings.useRelative">{{convertedBalance | formatDecimal}} {{'COMMON.UD' | translate}}<sub>{{walletData.parameters.currency | abbreviate}}</sub></span> @@ -83,7 +82,7 @@ <!-- Certifications --> <a class="item item-icon-left item-icon-right item-text-wrap ink" id="helptip-wallet-certifications" - ng-if="walletData.isMember" + ng-if="walletData.isMember||walletData.requirements.pendingMembership" ng-click="showCertifications()"> <i class="icon ion-ribbon-b"></i> <span clas="input-label">{{'ACCOUNT.CERTIFICATION_COUNT'|translate}}</span>