diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json index 1cbc9c67e890f50571e0ce35b0039fb174dbbcda..b12965d03c1d8adcbb5e4905fdfbe4f312d1b565 100644 --- a/www/i18n/locale-fr-FR.json +++ b/www/i18n/locale-fr-FR.json @@ -498,8 +498,8 @@ "ADD_QUESTION": "Ajouter une question personnalisée ", "BTN_CLEAN": "Vider", "BTN_RESET": "Réinitialiser", - "DEFINITELY_REVOKE": "Revoquer définitivement cette identité", - "DOWNLOAD_REVOKE": "Sauvegarder un fichier de revocation", + "DOWNLOAD_REVOKE": "Sauvegarder mon fichier de revocation", + "DOWNLOAD_REVOKE_HELP": "Disposer d'un fichier de révocation est important, par exemple en cas de perte de vos identifiants. Il vous permet de <b>sortir ce compte de la toile de confiance</b>, en redevenant ainsi un simple portefeuille.", "HELP_LEVEL": "Pour générer un fichier de sauvegarde de vos identifiants, choisissez <strong> au moins {{nb}} questions :</strong>", "LEVEL": "Niveau de sécurité", "LOW_LEVEL": "Faible <span class=\"hidden-xs\">(2 questions minimum)</span>", @@ -523,12 +523,17 @@ "QUESTION_17": "Quel est le premier album que vous avez acheté ?", "QUESTION_18": "Quel est le nom de votre équipe de sport préférée ?", "QUESTION_19": "Quel était le métier de votre grand-père ?", - "RECOVER_ID": "Recupérer vos identifiants", - "REVOCATION": "Révocation ...", + "REVOCATION_DOTS": "Révocation...", + "RECOVER_ID": "Retrouver mon mot de passe...", + "RECOVER_ID_HELP": "Si vous disposez d'un <b>fichier de sauvegarde de vos identifiants</b>, vous pouvez les retrouver en répondant correctement à vos questions personnelles.", + "REVOCATION_WITH_FILE": "Révoquer mon compte membre...", + "REVOCATION_WITH_FILE_HELP": "Si vous avez <b>définitivement perdus vos identifiants</b> de compte membre (ou que la sécurité du compte est compromise), vous pouvez utiliser <b>le fichier de révocation</b> du compte pour <b>forcer sa sortie définitive de la toile de confiance</b>.", + "REVOCATION_WALLET": "Révoquer immédiatement ce compte", + "REVOCATION_WALLET_HELP": "Demander la révocation de votre identité entraine la <b>sortie de la toile de confiance</b> (définitive pour le pseudonyme et la clé publique associés). Le compte ne pourra plus produire de Dividende Universel.<br/>Vous pourrez toutefois encore vous y connecter, comme à un simple portefeuille.", "REVOCATION_FILENAME": "revocation-{{uid}}-{{pubkey|formatPubkey}}-{{currency}}.txt", "REVOKE": "Révoquer cette identité", - "REVOKE_WITH_FILE": "Révoquer une identité à partir d'un fichier", - "SAVE_ID": "Sauvegarder vos identifiants", + "SAVE_ID": "Sauvegarder mes identifiants...", + "SAVE_ID_HELP": "Création d'un fichier de sauvegarde, pour <b>retrouver votre mot de passe</b> (et l'identifiant secret) <b>en cas de d'oubli</b>. Le fichier est <b>sécurisé</b> (chiffré) à l'aide de questions personnelles.", "STRONG_LEVEL": "Fort <span class=\"hidden-xs \">(6 questions minimum)</span>", "TITLE": "Compte et sécurité" }, @@ -628,11 +633,13 @@ "ONLY_SELF_CAN_EXECUTE_THIS_ACTION": "Vous devez avoir <b>publié votre identité</b> pour pouvoir effectuer cette action.", "GET_BLOCK_FAILED": "Echec de la récupération du bloc", "INVALID_BLOCK_HASH": "Bloc non trouvé (hash différent)", + "DOWNLOAD_REVOCATION_FAILED": "Echec du téléchargement du fichier de révocation.", "REVOCATION_FAILED": "Echec de la révocation.", "SALT_OR_PASSWORD_NOT_CONFIRMED": "Identifiant secret ou mot de passe incorrects", "RECOVER_ID_FAILED": "Echec de la récupération des identifiants", "LOAD_FILE_FAILED" : "Echec du chargement du fichier", "NOT_VALID_REVOCATION_FILE": "Fichier de revocation non valide (mauvais format de fichier)", + "NOT_VALID_SAVE_ID_FILE": "Fichier de récupération non valide (mauvais format de fichier)", "NOT_VALID_KEY_FILE": "Fichier de trousseau non valide (format non reconnu)", "EXISTING_ACCOUNT": "Vos identifiants correspondent à un compte déjà existant, dont la <a ng-click=\"showHelpModal('pubkey')\">clef publique</a> est :", "EXISTING_ACCOUNT_REQUEST": "Veuillez modifier vos identifiants afin qu'ils correspondent à un compte non utilisé.", diff --git a/www/js/controllers/login-controllers.js b/www/js/controllers/login-controllers.js index 93b751728321664732a74744bbd58307f5fbec1c..bb1bad1d9155c3b26e8ff5f7ba781ac7212e4f07 100644 --- a/www/js/controllers/login-controllers.js +++ b/www/js/controllers/login-controllers.js @@ -167,6 +167,10 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils, $timeout(csSettings.store, 500); } + if (parameters.success) { + parameters.success($scope.formData); + } + // hide loading if (parameters.silent) { UIUtils.loading.hide(); diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js index f375b5a26922db68b5e8cb25bc1e76b3bd8cc541..125d95dbc027796b3a7c715234b67627d404695d 100644 --- a/www/js/controllers/wallet-controllers.js +++ b/www/js/controllers/wallet-controllers.js @@ -776,13 +776,13 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr }; $scope.isLastSlide = false; $scope.smallscreen = UIUtils.screen.isSmall(); - $scope.option = 'recoverID'; $scope.recover = {}; $scope.isValidFile = false; $scope.isLogin = csWallet.isLogin(); $scope.hasSelf = csWallet.hasSelf(); + $scope.option = $scope.isLogin ? 'saveID' : 'recoverID'; $scope.formData = { addQuestion: '', @@ -821,8 +821,7 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr if (!formName) { formName = $scope.slides.slider.activeIndex === 1 && $scope.option == "saveID" ? 'questionsForm' : ($scope.slides.slider.activeIndex === 2 && $scope.option === "recoverID" ? 'recoverForm' : - ($scope.slides.slider.activeIndex === 2 && $scope.option === "saveID" ? 'answersForm' : - ($scope.slides.slider.activeIndex === 3 && $scope.option === "saveID" ? 'loginForm' : formName))); + ($scope.slides.slider.activeIndex === 2 && $scope.option === "saveID" ? 'answersForm' : formName)); if ($scope.slides.slider.activeIndex === 1 && $scope.option === "recoverID") { if ($scope.isValidFile) { @@ -832,7 +831,7 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr } else { - UIUtils.alert.error("ERROR.NOT_VALID_REVOCATION_FILE", "ERROR.LOAD_FILE_FAILED"); + UIUtils.alert.error("ERROR.NOT_VALID_SAVE_ID_FILE", "ERROR.LOAD_FILE_FAILED"); } } } @@ -845,8 +844,8 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr if(formName === 'recoverForm'){ $scope.recoverId(); } - else if(formName === 'loginForm'){ - $scope.submit(); + else if(formName === 'answersForm'){ + $scope.downloadSaveIDFile(); } else { $scope.slideNext(); @@ -973,31 +972,33 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr } }; - $scope.submit = function(){ - - if(!$scope.loginForm.$valid){ - return; - } - - var salt = $scope.formData.username; - var pwd = $scope.formData.password; - CryptoUtils.scryptKeypair(salt, pwd) - .then(function (keypair) { - $scope.pubkey = CryptoUtils.util.encode_base58(keypair.signPk); - }) - .then(function () { - if (!csWallet.isUserPubkey($scope.pubkey)) { - UIUtils.alert.error('ERROR.SALT_OR_PASSWORD_NOT_CONFIRMED', 'ERROR.LOGIN_FAILED'); - return; + $scope.downloadSaveIDFile = function(){ + // Force user re-auth + var loginData; + return csWallet.auth({ + forceAuth: true, + expectedPubkey: $scope.pubkey, + silent: true, + success: function(values) { + loginData = values; } + }) + .catch(function(err) { + if (err && err == 'CANCELLED') return; + UIUtils.alert.error('ERROR.SALT_OR_PASSWORD_NOT_CONFIRMED', 'ERROR.LOGIN_FAILED'); + return; + }) + .then(function(res) { + if (!res) return; + console.log(res); var file = { file: _.filter($scope.formData.questions, function (question) { return question.checked; }) }; var record = { - salt: $scope.formData.username, - pwd: $scope.formData.password, + salt: loginData.username, + pwd: loginData.password, questions: '', answer: '' }; @@ -1011,7 +1012,8 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr csWallet.downloadSaveId(record); $scope.closeModal(); }); - }); + }) + ; }; $scope.isRequired = function(){ @@ -1034,34 +1036,69 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr * Download revocation file */ $scope.downloadRevokeFile = function () { - csWallet.downloadRevocation(); + // Force re-authentication + return csWallet.auth({forceAuth: true}) + + // Download file + .then(function() { + return csWallet.downloadRevocation(); + }) + + .then(function() { + UIUtils.loading.hide(); + }) + + .catch(function(err){ + if (err && err == 'CANCELLED') return; + UIUtils.onError('ERROR.DOWNLOAD_REVOCATION_FAILED')(err); + }) + ; + }; /** - * Revoke identity + * Revoke wallet identity */ - $scope.revokeIdentity = function (confirm, confirmAgain) { + $scope.revokeWalletIdentity = function () { if (!$scope.hasSelf) { return UIUtils.alert.error("ERROR.ONLY_SELF_CAN_EXECUTE_THIS_ACTION"); } - if (!confirm) { - return UIUtils.alert.confirm("CONFIRM.REVOKE_IDENTITY", 'CONFIRM.POPUP_WARNING_TITLE', { - cssClass: 'warning', - okText: 'COMMON.BTN_YES', - okType: 'button-assertive' + + // Make sure user re-auth + return csWallet.auth({forceAuth: true}) + .then(function(confirm) { + UIUtils.loading.hide(); + if (!confirm) return; + return $scope.revokeIdentity(); }) + .catch(function (err) { + if (err == 'CANCELLED') return; + UIUtils.onError('ERROR.REVOCATION_FAILED')(err); + }); + }; + + /** + * Revoke identity + */ + $scope.revokeIdentity = function (confirm) { + + // Make sure user re-auth + confirm + if (!confirm) { + return UIUtils.alert.confirm("CONFIRM.REVOKE_IDENTITY", 'CONFIRM.POPUP_WARNING_TITLE', { + cssClass: 'warning', + okText: 'COMMON.BTN_YES', + okType: 'button-assertive' + }) .then(function (confirm) { - if (confirm) $scope.revokeIdentity(true); // loop with confirm - }); - } - if (!confirmAgain) { - return UIUtils.alert.confirm("CONFIRM.REVOKE_IDENTITY_2", 'CONFIRM.POPUP_TITLE', { - cssClass: 'warning', - okText: 'COMMON.BTN_YES', - okType: 'button-assertive' - }) + if (!confirm) return; + return UIUtils.alert.confirm("CONFIRM.REVOKE_IDENTITY_2", 'CONFIRM.POPUP_TITLE', { + cssClass: 'warning', + okText: 'COMMON.BTN_YES', + okType: 'button-assertive' + }); + }) .then(function (confirm) { - if (confirm) $scope.revokeIdentity(true, true); // loop with all confirmation + if (confirm) $scope.revokeIdentity(true, true); // loop with confirmation }); } @@ -1079,9 +1116,7 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, Cr $scope.closeModal(); return UIUtils.loading.hide(); }) - .catch(function (err) { - UIUtils.onError('ERROR.REVOCATION_FAILED')(err); - }); + .catch(UIUtils.onError('ERROR.REVOCATION_FAILED')); }; } diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js index f2a6d8dd156e6165c651ce03a16e9372bbe6452a..28a00c8dafe2c59f0e38540ec6a730873bd5b10c 100644 --- a/www/js/services/wallet-services.js +++ b/www/js/services/wallet-services.js @@ -106,7 +106,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se } var needLogin = !isLogin(); - var needAuth = options && options.auth && !isAuth(); + var needAuth = options && ((options.auth && !isAuth()) || options.forceAuth); // user already login if (!needLogin && !needAuth) { @@ -126,7 +126,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se authData = res; data.pubkey = res.pubkey; data.isNew = options && angular.isDefined(options.isNew) ? options.isNew : data.isNew; - if (csSettings.data.keepAuthIdle) { + if (keepAuth) { data.keypair = res.keypair || { signSk: null, signPk: null @@ -161,6 +161,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se if (options && options.silent) { UIUtils.loading.hide(); } + return keepAuth ? data : angular.merge({}, data, authData); }); }, @@ -200,7 +201,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se }); } - if (isAuth()) { + if (isAuth() && (!options || !options.forceAuth)) { return $q.when(data); } @@ -1414,10 +1415,14 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se var currency = res[0]; var revocation = res[1]; var revocationFile = new Blob([revocation], {type: 'text/plain; charset=utf-8'}); - return $translate('ACCOUNT.SECURITY.REVOCATION_FILENAME', {uid: data.uid, currency: currency.name, pubkey: data.pubkey}) - .then(function(fileName){ - FileSaver.saveAs(revocationFile, fileName); - }); + return $translate('ACCOUNT.SECURITY.REVOCATION_FILENAME', { + uid: data.uid, + currency: currency.name, + pubkey: data.pubkey + }) + .then(function (fileName) { + FileSaver.saveAs(revocationFile, fileName); + }); }); }, diff --git a/www/templates/wallet/modal_security.html b/www/templates/wallet/modal_security.html index 8055670b6e1fe1c2aa74f45ff845e62b21e0edbd..ac05530bd8b1b0a6b58b247c1a7225142b03d8c5 100644 --- a/www/templates/wallet/modal_security.html +++ b/www/templates/wallet/modal_security.html @@ -31,21 +31,65 @@ <!-- STEP 1 --> <ion-slide-page> <ion-content class="has-header padding"> + <div class="list"> - <div class="item card item-icon-right stable-bg padding ink dark" - ng-click="selectOption('saveID')" ng-if="isLogin" > - <i class="icon ion-ios-arrow-right"></i> - <span ng-bind-html="'ACCOUNT.SECURITY.SAVE_ID' | translate"></span> + + <div class="item item-complex card stable-bg item-icon-left item-icon-right ink" + ng-click="selectOption('recoverID')" ng-if="!isLogin"> + <div class="item-content item-text-wrap"> + <i class="item-image dark icon ion-person"></i> + <b class="ion-ios-undo icon-secondary dark" style="top: -8px; left: 39px; font-size: 12px;"></b> + <h2 translate>ACCOUNT.SECURITY.RECOVER_ID</h2> + <h4 class="gray" translate>ACCOUNT.SECURITY.RECOVER_ID_HELP</h4> + <i class="icon dark ion-ios-arrow-right"></i> + </div> + </div> + + <div class="item item-complex card stable-bg item-icon-left item-icon-right ink" + ng-click="selectOption('revocation')" ng-if="!isLogin"> + <div class="item-content item-text-wrap"> + <i class="item-image dark icon ion-person"></i> + <b class="ion-close icon-secondary dark" style="top: -8px; left: 39px; font-size: 12px;"></b> + <h2 translate>ACCOUNT.SECURITY.REVOCATION_WITH_FILE</h2> + <h4 class="gray" translate>ACCOUNT.SECURITY.REVOCATION_WITH_FILE_HELP</h4> + <i class="icon dark ion-ios-arrow-right"></i> + </div> + </div> + + + <div class="item item-complex card stable-bg item-icon-left item-icon-right ink" + ng-click="selectOption('saveID')" ng-if="isLogin"> + <div class="item-content item-text-wrap"> + <i class="item-image dark icon ion-person"></i> + <b class="ion-ios-redo icon-secondary dark" style="top: -8px; left: 39px; font-size: 12px;"></b> + <b class="ion-locked icon-secondary dark" style="top: 0px; left: 40px; font-size: 8px;"></b> + <h2 translate>ACCOUNT.SECURITY.SAVE_ID</h2> + <h4 class="gray" translate>ACCOUNT.SECURITY.SAVE_ID_HELP</h4> + <i class="icon dark ion-ios-arrow-right"></i> + </div> </div> - <div class="item card item-icon-right stable-bg padding ink dark" - ng-click="selectOption('recoverID')"> - <span ng-bind-html="'ACCOUNT.SECURITY.RECOVER_ID' | translate"></span> - <i class="icon ion-ios-arrow-right"></i> + + <div class="item item-complex card stable-bg item-icon-left item-icon-right ink hidden-xs hidden-sm" + ng-click="downloadRevokeFile()" ng-if="isLogin && hasSelf"> + <div class="item-content item-text-wrap"> + <i class="item-image dark icon ion-person"></i> + <b class="ion-ios-redo icon-secondary dark" style="top: -8px; left: 39px; font-size: 12px;"></b> + <b class="ion-close icon-secondary dark" style="top: 0px; left: 40px; font-size: 8px;"></b> + <h2 translate>ACCOUNT.SECURITY.DOWNLOAD_REVOKE</h2> + <h4 class="gray" translate>ACCOUNT.SECURITY.DOWNLOAD_REVOKE_HELP</h4> + <i class="icon dark ion-android-archive"></i> + </div> </div> - <div class="item card item-icon-right stable-bg padding ink dark" - ng-click="selectOption('revocation')"> - <span ng-bind-html="'ACCOUNT.SECURITY.REVOCATION' | translate"></span> - <i class="icon ion-ios-arrow-right"></i> + + + <div class="item item-complex card stable-bg item-icon-left item-icon-right ink" + ng-click="revokeWalletIdentity()" ng-if="isLogin"> + <div class="item-content item-text-wrap"> + <i class="item-image icon ion-person assertive-900"></i> + <b class="ion-close icon-secondary assertive-900" style="top: -8px; left: 39px; font-size: 12px;"></b> + <h2 translate>ACCOUNT.SECURITY.REVOCATION_WALLET</h2> + <h4 class="gray" translate>ACCOUNT.SECURITY.REVOCATION_WALLET_HELP</h4> + </div> </div> </div> @@ -58,10 +102,7 @@ </ion-slide-page> <ion-slide-page ng-if="option == 'revocation'"> - <ng-include src="'templates/wallet/slides/slides_revocation_1.html'"></ng-include> - </ion-slide-page> - <ion-slide-page ng-if="option == 'revocation'"> - <ng-include src="'templates/wallet/slides/slides_revocation_2.html'"></ng-include> + <ng-include src="'templates/wallet/slides/slides_revocation_file.html'"></ng-include> </ion-slide-page> <ion-slide-page ng-if="isLogin && option == 'saveID'"> diff --git a/www/templates/wallet/popover_actions.html b/www/templates/wallet/popover_actions.html index e8c1487c26544b44bec411b6034fe765ca9be999..108cf1b0b5bb909228325188b9f72fdf94f86bdc 100644 --- a/www/templates/wallet/popover_actions.html +++ b/www/templates/wallet/popover_actions.html @@ -56,7 +56,7 @@ <a class="item item-icon-left ink" ng-click="showSecurityModal()"> - <i class="icon ion-android-lock"></i> + <i class="icon ion-locked "></i> <span ng-bind-html="'ACCOUNT.BTN_SECURITY_DOTS' | translate"></span> </a> diff --git a/www/templates/wallet/slides/slides_revocation_1.html b/www/templates/wallet/slides/slides_revocation_1.html deleted file mode 100644 index 5d427b37c88ed7cb2945a1b3aefaa25a4acbb124..0000000000000000000000000000000000000000 --- a/www/templates/wallet/slides/slides_revocation_1.html +++ /dev/null @@ -1,27 +0,0 @@ -<ion-slide-page> - <ion-content class="has-header padding"> - <h3 translate>ACCOUNT.SECURITY.REVOCATION</h3> - <div class="list"> - <button class="item card item-icon-right stable-bg padding ink dark" - ng-click="downloadRevokeFile()" ng-if="isLogin && hasSelf"> - <i class="icon ion-android-archive"></i> - <span ng-bind-html="'ACCOUNT.SECURITY.DOWNLOAD_REVOKE' | translate"></span> - </button> - <div class="item card item-icon-right stable-bg padding ink dark" - ng-click="slideNext()"> - <i class="icon ion-ios-arrow-right"></i> - <span ng-bind-html="'ACCOUNT.SECURITY.REVOKE_WITH_FILE' | translate"></span> - </div> - <div class="item card item-icon-right stable-bg padding ink dark" - ng-click="revokeIdentity()" ng-if="isLogin && hasSelf"> - <i class="icon ion-minus-circled assertive"></i> - <span ng-bind-html="'ACCOUNT.SECURITY.DEFINITELY_REVOKE' | translate"></span> - </div> - </div> - <div class="padding hidden-xs text-right"> - <button class="button button-clear button-dark ink" ng-click="closeModal()" type="button" translate>COMMON.BTN_CANCEL - </button> - </div> - </ion-content> -</ion-slide-page> - diff --git a/www/templates/wallet/slides/slides_revocation_2.html b/www/templates/wallet/slides/slides_revocation_file.html similarity index 100% rename from www/templates/wallet/slides/slides_revocation_2.html rename to www/templates/wallet/slides/slides_revocation_file.html diff --git a/www/templates/wallet/slides/slides_saveID_2.html b/www/templates/wallet/slides/slides_saveID_2.html index 7015360026e5a90df675d0743a80bfd1629e0e21..f1acc6d67bfa1b269d96a47b33bd0cafe7808556 100644 --- a/www/templates/wallet/slides/slides_saveID_2.html +++ b/www/templates/wallet/slides/slides_saveID_2.html @@ -24,10 +24,9 @@ <button class="button button-clear button-dark" ng-click="restore()" type="button" translate>ACCOUNT.SECURITY.BTN_CLEAN </button> - <button class="button button-calm icon-right ion-chevron-right ink" - type="submit" translate> - COMMON.BTN_NEXT - <i class="icon ion-arrow-right-a"></i> + <button class="button button-positive ink" type="submit" translate> + COMMON.BTN_CONTINUE + <i class="icon ion-android-archive"></i> </button> </div> </div> diff --git a/www/templates/wallet/slides/slides_saveID_3.html b/www/templates/wallet/slides/slides_saveID_3.html deleted file mode 100644 index 4aa63dc0435c728684c7ce18fba3eec532d1cb84..0000000000000000000000000000000000000000 --- a/www/templates/wallet/slides/slides_saveID_3.html +++ /dev/null @@ -1,72 +0,0 @@ - <ion-content> - <form name="loginForm" novalidate="" ng-submit="submit()"> - - <div class="list" ng-init="setForm(loginForm, 'loginForm')"> - - <!-- salt (=username, to enable browser login cache) --> - <label class="item item-input" - ng-class="{ 'item-input-error': loginForm.$submitted && loginForm.username.$invalid}"> - <span class="input-label hidden-xs" translate>LOGIN.SALT</span> - <input ng-if="!showSalt" - name="username" type="{{showSalt ? 'text' : 'password'}}" placeholder="{{'LOGIN.SALT_HELP' | translate}}" - ng-model="formData.username" - ng-model-options="{ debounce: 650 }" - class="highlight-light" - required> - <input ng-if="showSalt" - name="username" type="text" placeholder="{{'LOGIN.SALT_HELP' | translate}}" - ng-model="formData.username" - ng-model-options="{ debounce: 650 }" - class="highlight-light" - required> - </label> - <div class="form-errors" - ng-show="loginForm.$submitted && loginForm.username.$error" - ng-messages="loginForm.username.$error"> - <div class="form-error" ng-message="required"> - <span translate="ERROR.FIELD_REQUIRED"></span> - </div> - </div> - - <!-- Show salt --> - <div class="item item-toggle dark"> - <span translate>LOGIN.SHOW_SALT</span> - <label class="toggle toggle-stable"> - <input type="checkbox" ng-model="showSalt"> - <div class="track"> - <div class="handle"></div> - </div> - </label> - </div> - - <!-- password--> - <label class="item item-input" - ng-class="{ 'item-input-error': loginForm.$submitted && loginForm.password.$invalid}"> - <span class="input-label hidden-xs" translate>LOGIN.PASSWORD</span> - <input name="password" type="password" placeholder="{{'LOGIN.PASSWORD_HELP' | translate}}" - ng-model="formData.password" - ng-model-options="{ debounce: 650 }" - select-on-click - required> - </label> - <div class="form-errors" - ng-show="loginForm.$submitted && loginForm.password.$error" - ng-messages="loginForm.password.$error"> - <div class="form-error" ng-message="required"> - <span translate="ERROR.FIELD_REQUIRED"></span> - </div> - </div> - </div> - - <div class="padding hidden-xs text-right"> - <button class="button button-clear button-dark ink" ng-click="closeModal()" - type="button" translate>COMMON.BTN_CANCEL - </button> - <button class="button button-positive ink" type="submit" translate> - COMMON.BTN_SEND - <i class="icon ion-android-send"></i> - </button> - </div> - </form> - </ion-content> -