Commit 321b9e7c authored by Benoit Lavenier's avatar Benoit Lavenier

[fix] Fix select file, in login, recover ID, and revocation drop-zone (need for web extension)

[fix] Fix locale image flag
parent c364c479
......@@ -2259,7 +2259,7 @@ $ionicon-var-badge-editable: $ionicon-var-edit + "\00a0";
height: 44px;
padding-top: 6px;
padding-bottom: 6px;
img {
.flag-image {
position: relative;
bottom: 0;
width: 32px !important;
......@@ -2382,7 +2382,7 @@ $ionicon-var-badge-editable: $ionicon-var-edit + "\00a0";
Drop zone component
**********/
div[dropzone] {
div[drop-zone] {
border: 2px dashed #bbb;
border-radius: 5px;
padding: 15px;
......@@ -2398,7 +2398,7 @@ div[dropzone] {
}
}
div[dropzone]:hover {
div[drop-zone]:hover {
background-color: $stable-100-bg;
}
......
......@@ -146,6 +146,7 @@
"USE_WALLETS_ENCRYPTION": "Secure the list",
"USE_WALLETS_ENCRYPTION_HELP": "Enables you to encrypt the list of your wallets. Authentication required to access it.",
"ENABLE_HELPTIP": "Enable contextual help tips",
"DISABLE_HELPTIP": "Disable contextual help tips",
"ENABLE_UI_EFFECTS": "Enable visual effects",
"HISTORY_SETTINGS": "Account operations",
"DISPLAY_UD_HISTORY": "Display produced dividends?",
......@@ -617,7 +618,8 @@
"QUESTION_19": "What was your grand-father's job ?",
"RECOVER_ID": "Recover my password...",
"RECOVER_ID_HELP": "If you have a <b>backup file of your identifiers</b>, you can find them by answering your personal questions correctly.",
"REVOCATION_WITH_FILE" : "Rekoke my member account...",
"RECOVER_ID_SELECT_FILE": "Select the <b>backup file of your identifiers</b> to use:",
"REVOCATION_WITH_FILE" : "Revoke my member account...",
"REVOCATION_WITH_FILE_DESCRIPTION": "If you have <b>permanently lost your member account credentials (or if account security is compromised), you can use <b>the revocation file</b> of the account <b>to quit the Web Of Trust</b>.",
"REVOCATION_WITH_FILE_HELP": "To <b>permanently revoke</ b> a member account, please drag the revocation file in the box below, or click in the box to search for a file.",
"REVOCATION_WALLET": "Revoke this account immediately",
......
......@@ -618,7 +618,8 @@
"QUESTION_19": "What was your grand-father's job ?",
"RECOVER_ID": "Recover my password...",
"RECOVER_ID_HELP": "If you have a <b>backup file of your identifiers</b>, you can find them by answering your personal questions correctly.",
"REVOCATION_WITH_FILE" : "Rekoke my member account...",
"RECOVER_ID_SELECT_FILE": "Select the <b>backup file of your identifiers</b> to use:",
"REVOCATION_WITH_FILE" : "Revoke my member account...",
"REVOCATION_WITH_FILE_DESCRIPTION": "If you have <b>permanently lost your member account credentials (or if account security is compromised), you can use <b>the revocation file</b> of the account <b>to quit the Web Of Trust</b>.",
"REVOCATION_WITH_FILE_HELP": "To <b>permanently revoke</ b> a member account, please drag the revocation file in the box below, or click in the box to search for a file.",
"REVOCATION_WALLET": "Revoke this account immediately",
......
......@@ -618,6 +618,7 @@
"QUESTION_19": "Quel était le métier de votre grand-père ?",
"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.",
"RECOVER_ID_SELECT_FILE": "Choisissez le <b>fichier de sauvegarde de vos identifiants</b> à utiliser :",
"REVOCATION_WITH_FILE": "Révoquer mon compte membre...",
"REVOCATION_WITH_FILE_DESCRIPTION": "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_WITH_FILE_HELP": "Pour <b>révoquer définitivement</b> un compte membre, veuillez glisser dans la zone ci-dessous votre fichier de révocation, ou bien cliquer dans la zone pour rechercher un fichier.",
......
......@@ -98,7 +98,7 @@ angular.module("cesium.config", [])
}
},
"version": "1.5.11",
"build": "2020-03-12T14:50:16.897Z",
"build": "2020-03-13T17:20:00.591Z",
"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
})
......
......@@ -656,7 +656,7 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
$scope.showLocalesPopover = function(event) {
UIUtils.popover.show(event, {
templateUrl: 'templates/api/locales_popover.html',
templateUrl: 'templates/common/popover_locales.html',
scope: $scope,
autoremove: true,
afterShow: function(popover) {
......
......@@ -43,7 +43,7 @@ function LoginController($scope, $timeout, $controller, csWallet) {
}
function LoginModalController($scope, $timeout, $q, $ionicPopover, $document, CryptoUtils, csCrypto, ionicReady,
function LoginModalController($scope, $timeout, $q, $ionicPopover, $window, CryptoUtils, csCrypto, ionicReady,
UIUtils, BMA, Modals, csSettings, Device, parameters) {
'ngInject';
......@@ -506,14 +506,17 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $document, Cr
});
};
$scope.fileChanged = function(event) {
$scope.validatingFile = true;
$scope.formData.file = event && event.target && event.target.files && event.target.files.length && event.target.files[0];
if (!$scope.formData.file) {
$scope.onFileChanged = function(file) {
if (!file || !file.fileData) {
$scope.validatingFile = false;
return;
return; // Skip
}
$scope.formData.file = {
name: file.fileData.name,
size: file.fileData.size,
content: file.fileContent
};
$scope.validatingFile = true;
$timeout(function() {
console.debug("[login] key file changed: ", $scope.formData.file);
$scope.validatingFile = true;
......@@ -544,51 +547,6 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, $document, Cr
});
};
/**
* On the file chooser
*/
$scope.openFileChooser = function() {
var elements = angular.element(document.getElementById('loginImportFile'));
if (elements && elements.length) {
elements[0].click();
}
}
/**
* On file drop
*/
$scope.onKeyFileDrop = function(file) {
if (!file || !file.fileData) return;
$scope.formData.file = {
name: file.fileData.name,
size: file.fileData.size,
content: file.fileContent
};
$scope.validatingFile = true;
$timeout(function() {
return $scope.readKeyFile($scope.formData.file, {withSecret: false})
.then(function (keypair) {
if (!keypair || !keypair.signPk) {
$scope.formData.file.valid = false;
$scope.formData.file.pubkey = undefined;
}
else {
$scope.formData.file.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
$scope.formData.file.valid = !$scope.expectedPubkey || $scope.expectedPubkey == $scope.formData.file.pubkey;
$scope.validatingFile = false;
}
})
.catch(function (err) {
$scope.validatingFile = false;
$scope.formData.file.valid = false;
$scope.formData.file.pubkey = undefined;
UIUtils.onError('ERROR.AUTH_FILE_ERROR')(err);
});
});
};
$scope.removeKeyFile = function() {
$scope.formData.file = undefined;
};
......
......@@ -1267,13 +1267,12 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, pa
};
/**
* Recover Id
* Set the file content
*/
$scope.recoverContent = function(file) {
$scope.onFileChanged = function(file) {
$scope.hasContent = angular.isDefined(file) && file !== '';
$scope.fileData = file.fileData ? file.fileData : '';
$scope.isValidFile = $scope.fileData !== '' && $scope.fileData.type == 'text/plain';
$scope.isValidFile = $scope.fileData !== '' && $scope.fileData.type === 'text/plain';
if ($scope.isValidFile && $scope.option === 'recoverID') {
$scope.content = file.fileContent.split('\n');
......@@ -1325,7 +1324,8 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, pa
else {
UIUtils.alert.error('ERROR.RECOVER_ID_FAILED');
}
});
})
.catch(UIUtils.onError('ERROR.RECOVER_ID_FAILED'));
};
......@@ -1482,7 +1482,16 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate, pa
$scope.closeModal();
return UIUtils.loading.hide();
})
.catch(UIUtils.onError('ERROR.REVOCATION_FAILED'));
.catch(function(err) {
if ($scope.revocation){
$scope.isValidFile = false;
$scope.revocationError = err && err.message || err || 'ERROR.REVOCATION_FAILED';
UIUtils.loading.hide(10);
}
else {
UIUtils.onError('ERROR.REVOCATION_FAILED')(err);
}
});
};
......
......@@ -755,7 +755,8 @@ function WalletListImportModalController($scope, $timeout, BMA, csWallet) {
$scope.isValidFile = false;
$scope.validatingFile = false;
$scope.importFromFile = function(file) {
$scope.onFileChanged = function(file) {
console.log(file);
$scope.validatingFile = true;
$scope.hasContent = angular.isDefined(file) && file !== '';
......
......@@ -368,49 +368,97 @@ angular.module('cesium.directives', [])
};
})
.directive("dropzone", function($parse) {
.directive("dropZone", function($parse) {
return {
restrict: 'A',
scope: false,
link: function(scope, elem, attrs) {
var fn = $parse(attrs.dropzone);
elem.bind('dragover', function (e) {
e.stopPropagation();
e.preventDefault();
});
elem.bind('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
});
elem.bind('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
});
elem.bind('drop', function(e) {
e.stopPropagation();
e.preventDefault();
var fileData = {
name: e.dataTransfer.files[0].name,
size: e.dataTransfer.files[0].size,
type: e.dataTransfer.files[0].type
};
var reader = new FileReader();
reader.onload = function(onLoadEvent) {
scope.$apply(function () {
fn(scope, {
file: {
fileContent: onLoadEvent.target.result,
fileData : fileData}
});
link: function(scope, elem, attrs) {
var fn = $parse(attrs.dropZone);
elem.bind('dragover', function (e) {
e.stopPropagation();
e.preventDefault();
});
elem.bind('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
});
elem.bind('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
});
elem.bind('drop', function(e) {
e.stopPropagation();
e.preventDefault();
var fileData = {
name: e.dataTransfer.files[0].name,
size: e.dataTransfer.files[0].size,
type: e.dataTransfer.files[0].type
};
var reader = new FileReader();
reader.onload = function(onLoadEvent) {
scope.$apply(function () {
fn(scope, {
file: {
fileContent: onLoadEvent.target.result,
fileData : fileData}
});
};
reader.readAsText(e.dataTransfer.files[0]);
});
});
};
reader.readAsText(e.dataTransfer.files[0]);
});
}
};
})
// See http://embed.plnkr.co/2vgnFe/
.directive('fileSelect', function ($parse) {
'use strict';
return {
restrict: 'A',
scope: false,
template: '<input type="file" style="display: none;" />' +
'<ng-transclude></ng-transclude>',
transclude: true,
link: function (scope, element, attrs) {
var fn = $parse(attrs.fileSelect);
var fileInput = element.children('input[file]');
if (attrs.accept) {
fileInput[0].accept = attrs.accept;
}
fileInput.on('change', function (onChangeEvent) {
var reader = new FileReader();
var fileData = {
name: this.files[0].name,
size: this.files[0].size,
type: this.files[0].type
};
reader.onload = function(onLoadEvent) {
scope.$applyAsync(function() {
fn(scope, {
file: {
fileContent: onLoadEvent.target.result,
fileData : fileData}
});
});
};
reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
});
element.on('click', function () {
fileInput[0].click();
});
}
};
})
// Un-authenticate when window closed
// see: https://stackoverflow.com/questions/28197316/javascript-or-angularjs-defer-browser-close-or-tab-close-between-refresh
.directive('windowExitUnauth', function($window, csSettings, csWallet) {
......@@ -427,5 +475,5 @@ angular.module('cesium.directives', [])
});
}
};
})
});
;
......@@ -35,7 +35,12 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
.config(function($compileProvider, csConfig) {
'ngInject';
$compileProvider.debugInfoEnabled(csConfig.debug === true);
$compileProvider.debugInfoEnabled(csConfig.debug === true);
// Fix issue #893
// See https://stackoverflow.com/questions/31859257/firefox-addon-using-angularjs-ng-src-not-working
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(filesystem:resource|resource|moz-extension|chrome-extension|file):/);
})
.config(function($animateProvider) {
......
......@@ -1749,6 +1749,9 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
},
recoverId = function(recover) {
if (!recover || !recover.cypherNonce || !recover.cypherSalt || !recover.cypherPwd) {
throw {message:'ERROR.INVALID_FILE_FORMAT'};
}
var nonce = CryptoUtils.util.decode_base58(recover.cypherNonce);
return getkeypairSaveId(recover)
.then(function (recover) {
......@@ -1763,7 +1766,8 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
return recover;
})
.catch(function(err){
console.warn('Incorrect answers - Unable to recover passwords');
console.warn('Incorrect answers: unable to recover identifiers', err);
throw new Error('Incorrect answers: unable to recover identifiers');
});
},
......
......@@ -7,8 +7,8 @@
<!-- locales -->
<button class="button button-clear hidden-xs hidden-sm flag"
ng-click="showLocalesPopover($event)" style="align-content: center">
<img ng-src="../img/flag-{{$root.settings.locale.flag}}.png"/>
<b class="icon-secondary ion-arrow-down-b gray"></b>
<i class="flag-image" style="background-image: url(../img/flag-{{$root.settings.locale.flag}}.png);"></i>
<b class="ion-arrow-down-b gray"></b>
</button>
</ion-nav-buttons>
......
......@@ -6,7 +6,7 @@
<!-- locales -->
<button class="button button-clear hidden-xs hidden-sm flag"
ng-click="showLocalesPopover($event)" style="align-content: center">
<img ng-src="./img/flag-{{$root.settings.locale.flag}}.png"/>
<i class="flag-image" style="background-image: url(./img/flag-{{$root.settings.locale.flag}}.png);"></i>
<b class="ion-arrow-down-b gray"></b>
</button>
</ion-nav-buttons>
......
......@@ -7,16 +7,13 @@
<span class="positive" translate>LOGIN.FILE.HELP</span>
</div>
<div dropzone="onKeyFileDrop(file)">
<div ng-if="!formData.file" ng-click="openFileChooser()">
<div drop-zone="onFileChanged(file)">
<div ng-if="!formData.file" file-select="onFileChanged(file)" accept=".dunikey,.yml">
<h2 class="gray" translate>COMMON.CHOOSE_FILE</h2>
<input type="file" id="loginImportFile" accept=".dunikey,.yml"
style="visibility:hidden; position:absolute;"
onchange="angular.element(this).scope().fileChanged(event)"/>
</div>
<div class="item item-icon-left item-icon-right stable-bg"
ng-if="formData.file" >
ng-if="formData.file" >
<i class="icon ion-document-text dark"></i>
<div class="item-content row">
<div class="col">
......
<ion-modal-view id="transfer" class="modal-full-height">
<ion-header-bar class="bar-positive">
<button class="button button-clear" ng-click="closeModal()" translate>COMMON.BTN_CANCEL</button>
<button class="button button-clear visible-xs" ng-click="closeModal()" translate>COMMON.BTN_CANCEL</button>
<h1 class="title" translate>ACCOUNT.WALLET_LIST.IMPORT_MODAL.TITLE</h1>
</ion-header-bar>
......@@ -8,11 +8,11 @@
<p translate>ACCOUNT.WALLET_LIST.IMPORT_MODAL.HELP</p>
<div dropzone="importFromFile(file)">
<div ng-if="!hasContent" onclick="angular.element(document.querySelector('#walletsImportFile'))[0].click();">
<div drop-zone="onFileChanged(file)">
<div ng-if="!hasContent"
file-select="onFileChanged(file)"
accept=".csv,.txt">
<h2 class="gray" translate>COMMON.CHOOSE_FILE</h2>
<input type="file" id="walletsImportFile" accept=".csv,.txt"
style="visibility:hidden; position:absolute;" on-read-file="importFromFile(file)"/>
</div>
<div ng-if="hasContent" class="item item-icon-left item-icon-right stable-bg">
......
<ion-content class="has-header padding" >
<h3 translate>ACCOUNT.SECURITY.RECOVER_ID</h3>
<p translate>ACCOUNT.SECURITY.RECOVER_ID_SELECT_FILE</p>
<div dropzone="recoverContent(file)">
<div ng-if="!hasContent" onclick="angular.element(document.querySelector('#saveIdFile'))[0].click();">
<div drop-zone="onFileChanged(file)">
<div ng-if="!hasContent" file-select="onFileChanged(file)" accept=".txt">
<h2 class="gray" translate>COMMON.CHOOSE_FILE</h2>
<input type="file" id="saveIdFile" accept=".txt"
style="visibility:hidden; position:absolute;" on-read-file="recoverContent(file)"/>
</div>
<div ng-if="hasContent" class="item row item-icon-left no-padding">
<i class="icon ion-document-text gray"></i>
<div class="col">
<span>{{fileData.name}}</span>
<br />
<small>{{fileData.size}} Ko</small>
</div>
<div class="col-10">
<b ng-class="{'ion-android-done balanced': isValidFile,'ion-close-circled assertive': !isValidFile}"
style="font-size: 28px; position: relative; top: 6px;"></b>
<button class="button-icon ion-close-round gray pull-right" style="font-size:10px;"
ng-click="restore()"></button>
</div>
<div ng-if="hasContent" class="item item-icon-left item-icon-right">
<i class="icon ion-document-text dark"></i>
<div class="item-content row">
<div class="col ">
<span>{{fileData.name}}</span>
<br />
<small>{{fileData.size}} Ko</small>
</div>
<div class="col-10">
<b ng-class="{'ion-close-circled assertive': !isValidFile}"
style="font-size: 28px; position: relative; top: 6px;"></b>
</div>
</div>
<!-- remove file button -->
<a class="ion-close-round gray pull-right" style="font-size: 10px; position: absolute; top: 6px; right: 6px;"
ng-click="restore()"></a>
</div>
</div>
......
<ion-content class="has-header padding" >
<p translate>ACCOUNT.SECURITY.REVOCATION_WITH_FILE_HELP</p>
<div dropzone="recoverContent(file)">
<div ng-if="!hasContent" onclick="angular.element(document.querySelector('#revocationFile'))[0].click();">
<div drop-zone="onFileChanged(file)">
<div ng-if="!hasContent" file-select="onFileChanged(file)" accept=".txt">
<h2 class="gray" translate>COMMON.CHOOSE_FILE</h2>
<input type="file" id="revocationFile" accept=".txt"
style="visibility:hidden; position:absolute;" on-read-file="recoverContent(file)"/>
</div>
<div ng-if="hasContent" class="item row item-icon-left no-padding">
<i class="icon ion-document-text gray"></i>
<div class="col">
<span>{{fileData.name}}</span>
<br />
<small>{{fileData.size}} Ko</small>
</div>
<div class="col-10">
<b ng-class="{'ion-android-done balanced': isValidFile,'ion-close-circled assertive': !isValidFile}"
style="font-size: 28px; position: relative; top: 6px;"></b>
<button class="button-icon ion-close-round gray pull-right" style="font-size:10px;"
ng-click="restore()"></button>
<div ng-if="hasContent" class="item item-icon-left item-icon-right">
<i class="icon ion-document-text dark"></i>
<div class="row">
<div class="col">
<h2>{{fileData.name}}</h2>
<h4 ng-if="fileData.lastModified">
<span class="gray" translate>COMMON.FILE.DATE</span> {{fileData.lastModified/1000|formatDate}}
</h4>
<h5>
<span class="gray" translate>COMMON.FILE.SIZE</span> {{fileData.size|formatInteger}} Ko
</h5>
</div>
<div class="col padding-left">
<h3 class="assertive animate-show-hide ng-hide" ng-show="!isValidFile"><br/>
<i class="ion-close-circled assertive"></i>
{{revocationError|translate}}
</h3>
</div>
</div>
<!-- remove file button -->
<a class="ion-close-round gray pull-right" style="font-size: 10px; position: absolute; top: 6px; right: 6px;"
ng-click="restore()"></a>
</div>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment