mirror of
https://github.com/nextcloud/passman.git
synced 2025-10-19 18:06:09 +08:00
Merge branch 'import-export-improvements'
Signed-off-by: binsky <timo@binsky.org>
This commit is contained in:
commit
0e09b4f579
11 changed files with 234 additions and 151 deletions
|
@ -1276,10 +1276,10 @@ h3 {
|
||||||
.setting-group input[type="text"], .setting-group input[type="password"], .setting-group textarea {
|
.setting-group input[type="text"], .setting-group input[type="password"], .setting-group textarea {
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
|
|
||||||
.setting-group.margin-bottom-25 {
|
.setting-group.margin-bottom-25, .margin-bottom-25 {
|
||||||
margin-bottom: 25px; }
|
margin-bottom: 25px; }
|
||||||
|
|
||||||
.setting-group.margin-bottom-10 {
|
.setting-group.margin-bottom-10, .margin-bottom-10 {
|
||||||
margin-bottom: 10px; }
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
.display-grid {
|
.display-grid {
|
||||||
|
@ -1295,7 +1295,7 @@ h3 {
|
||||||
label[for=confirmVaultPWChange] {
|
label[for=confirmVaultPWChange] {
|
||||||
margin-bottom: 10px; }
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
label[for=confirmVaultDelete] {
|
label[for=confirmVaultDelete], label[for=skipFirstRow] {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px; }
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -32,8 +32,8 @@
|
||||||
* Controller of the passmanApp
|
* Controller of the passmanApp
|
||||||
*/
|
*/
|
||||||
angular.module('passmanApp')
|
angular.module('passmanApp')
|
||||||
.controller('GenericCsvImportCtrl', ['$scope', 'CredentialService', 'FileService', 'EncryptService', '$translate', '$q',
|
.controller('GenericCsvImportCtrl', ['$scope', '$rootScope', 'CredentialService', 'FileService', 'EncryptService', '$translate', '$q',
|
||||||
function ($scope, CredentialService, FileService, EncryptService, $translate, $q) {
|
function ($scope, $rootScope, CredentialService, FileService, EncryptService, $translate, $q) {
|
||||||
$scope.hello = 'world';
|
$scope.hello = 'world';
|
||||||
|
|
||||||
$scope.credentialProperties = [
|
$scope.credentialProperties = [
|
||||||
|
@ -45,17 +45,27 @@
|
||||||
{
|
{
|
||||||
label: 'Username',
|
label: 'Username',
|
||||||
prop: 'username',
|
prop: 'username',
|
||||||
matching: ['username', 'user', 'login', 'login name']
|
matching: ['username', 'user', 'login', 'login name', 'login_username']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Password',
|
label: 'Password',
|
||||||
prop: 'password',
|
prop: 'password',
|
||||||
matching: ['password', 'pass', 'pw']
|
matching: ['password', 'pass', 'pw', 'login_password']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'TOTP Secret',
|
label: 'TOTP Secret or Object',
|
||||||
prop: 'otp',
|
prop: 'otp',
|
||||||
matching: ['totp']
|
matching: ['otp', 'otp_object', 'totp', 'login_totp']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
prop: 'email',
|
||||||
|
matching: ['email', 'mail']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Notes',
|
||||||
|
prop: 'description',
|
||||||
|
matching: ['notes', 'description', 'comments']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Custom field',
|
label: 'Custom field',
|
||||||
|
@ -71,24 +81,45 @@
|
||||||
prop: 'files',
|
prop: 'files',
|
||||||
matching: ['files']
|
matching: ['files']
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Notes',
|
|
||||||
prop: 'description',
|
|
||||||
matching: ['notes', 'description', 'comments']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Email',
|
|
||||||
prop: 'email',
|
|
||||||
matching: ['email', 'mail']
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'URL',
|
label: 'URL',
|
||||||
prop: 'url',
|
prop: 'url',
|
||||||
matching: ['website', 'url', 'fulladdress', 'site', 'web site']
|
matching: ['website', 'url', 'fulladdress', 'site', 'web site', 'login_uri']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Tags',
|
label: 'Tags',
|
||||||
prop: 'tags'
|
prop: 'tags',
|
||||||
|
matching: ['tags', 'folder']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Created',
|
||||||
|
prop: 'created',
|
||||||
|
matching: ['created', 'creation']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Changed',
|
||||||
|
prop: 'changed',
|
||||||
|
matching: ['changed', 'edited']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Expire time',
|
||||||
|
prop: 'expire_time',
|
||||||
|
matching: ['expire_time', 'expire', 'expires', 'expires_at']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete time',
|
||||||
|
prop: 'delete_time',
|
||||||
|
matching: ['delete_time', 'delete', 'deleted_at']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Icon',
|
||||||
|
prop: 'icon',
|
||||||
|
matching: ['icon', 'favicon']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Compromised',
|
||||||
|
prop: 'compromised',
|
||||||
|
matching: ['compromised']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Ignored',
|
label: 'Ignored',
|
||||||
|
@ -99,14 +130,23 @@
|
||||||
return {text: t};
|
return {text: t};
|
||||||
};
|
};
|
||||||
var rowToCredential = async function (row) {
|
var rowToCredential = async function (row) {
|
||||||
var _credential = PassmanImporter.newCredential();
|
let _credential = PassmanImporter.newCredential();
|
||||||
for(var k = 0; k < $scope.import_fields.length; k++){
|
for(let k = 0; k < $scope.import_fields.length; k++){
|
||||||
var field = $scope.import_fields[k];
|
const field = $scope.import_fields[k];
|
||||||
if(field){
|
if(field){
|
||||||
if(field === 'otp'){
|
if(field === 'otp'){
|
||||||
_credential.otp.secret = row[k];
|
if (typeof row[k] === 'object' || row[k].includes('{"')) {
|
||||||
|
const otpobj = JSON.parse(row[k]);
|
||||||
|
if (typeof otpobj === 'object' && otpobj.secret !== undefined && otpobj.algorithm !== undefined && otpobj.period !== undefined && otpobj.digits !== undefined) {
|
||||||
|
_credential.otp = otpobj;
|
||||||
|
} else if (otpobj.secret !== undefined) {
|
||||||
|
_credential.otp.secret = otpobj.secret;
|
||||||
|
}
|
||||||
|
} else if (row[k] !== '{}') {
|
||||||
|
_credential.otp.secret = row[k];
|
||||||
|
}
|
||||||
} else if(field === 'custom_field'){
|
} else if(field === 'custom_field'){
|
||||||
var key = ($scope.matched) ? $scope.parsed_csv[0][k] : 'Custom field '+ k;
|
const key = ($scope.matched) ? $scope.parsed_csv[0][k] : 'Custom field '+ k;
|
||||||
_credential.custom_fields.push({
|
_credential.custom_fields.push({
|
||||||
'label': key,
|
'label': key,
|
||||||
'value': row[k],
|
'value': row[k],
|
||||||
|
@ -116,46 +156,44 @@
|
||||||
if (row[k] !== undefined && (typeof row[k] === 'string' || row[k] instanceof String) && row[k].length > 1){
|
if (row[k] !== undefined && (typeof row[k] === 'string' || row[k] instanceof String) && row[k].length > 1){
|
||||||
try {
|
try {
|
||||||
row[k] = JSON.parse(row[k]);
|
row[k] = JSON.parse(row[k]);
|
||||||
for(let i = 0; k < row[k].length; i++){
|
|
||||||
_credential.custom_fields.push({
|
|
||||||
'label': row[k][i].label,
|
|
||||||
'secret': row[k][i].secret,
|
|
||||||
'field_type': row[k][i].field_type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore row[k], it contains no valid json data
|
// ignore row[k], it contains no valid json data
|
||||||
// console.error(e);
|
console.error(e);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
for(let j = 0; j < row[k].length; j++){
|
for(let j = 0; j < row[k].length; j++){
|
||||||
if (row[k][j].field_type === 'file'){
|
if (row[k][j].field_type === 'file'){
|
||||||
var _file = {
|
const _file = {
|
||||||
filename: row[k][j].value.filename,
|
filename: row[k][j].value.filename,
|
||||||
size: row[k][j].value.size,
|
size: row[k][j].value.size,
|
||||||
mimetype: row[k][j].value.mimetype,
|
mimetype: row[k][j].value.mimetype,
|
||||||
data: row[k][j].value.file_data
|
data: row[k][j].value.file_data ? row[k][j].value.file_data : row[k][j].value.data
|
||||||
};
|
};
|
||||||
|
if (_file.data === undefined) {
|
||||||
row[k][j].value = await FileService.uploadFile(_file).then(FileService.getEmptyFileWithDecryptedFilename);
|
console.error('Unable to parse file data from ', row[k][j]);
|
||||||
|
$scope.log.push('Unable to parse file data from file ' + _file.filename);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
_credential.custom_fields.push(row[k][j]);
|
row[k][j].value = await FileService.uploadFile(_file).then(FileService.getEmptyFileWithDecryptedFilename);
|
||||||
}
|
}
|
||||||
|
_credential.custom_fields.push(row[k][j]);
|
||||||
}
|
}
|
||||||
} else if(field === 'files'){
|
} else if(field === 'files'){
|
||||||
if (row[k] !== undefined && (typeof row[k] === 'string' || row[k] instanceof String) && row[k].length > 1){
|
if (row[k] !== undefined && (typeof row[k] === 'string' || row[k] instanceof String) && row[k].length > 1){
|
||||||
try {
|
try {
|
||||||
row[k] = JSON.parse(row[k]);
|
row[k] = JSON.parse(row[k]);
|
||||||
for(let i = 0; k < row[k].length; i++){
|
for(let i = 0; i < row[k].length; i++){
|
||||||
_credential.files.push({
|
_credential.files.push(await FileService.uploadFile({
|
||||||
filename: row[k][i].filename,
|
filename: row[k][i].filename,
|
||||||
size: row[k][i].size,
|
size: row[k][i].size,
|
||||||
mimetype: row[k][i].mimetype
|
mimetype: row[k][i].mimetype,
|
||||||
});
|
data: row[k][i].file_data ? row[k][i].file_data : row[k][i].data
|
||||||
|
}).then(FileService.getEmptyFileWithDecryptedFilename));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore row[k], it contains no valid json data
|
// ignore row[k], it contains no valid json data
|
||||||
// console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(let j = 0; j < row[k].length; j++){
|
for(let j = 0; j < row[k].length; j++){
|
||||||
|
@ -163,15 +201,25 @@
|
||||||
filename: row[k][j].filename,
|
filename: row[k][j].filename,
|
||||||
size: row[k][j].size,
|
size: row[k][j].size,
|
||||||
mimetype: row[k][j].mimetype,
|
mimetype: row[k][j].mimetype,
|
||||||
data: row[k][j].file_data
|
data: row[k][j].file_data ? row[k][j].file_data : row[k][j].data
|
||||||
}).then(FileService.getEmptyFileWithDecryptedFilename));
|
}).then(FileService.getEmptyFileWithDecryptedFilename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(field === 'tags'){
|
} else if(field === 'tags'){
|
||||||
if( row[k]) {
|
if(row[k] && row[k] !== '' && row[k] !== '[]') {
|
||||||
var tags = row[k].split(',');
|
if (row[k].startsWith('[') && row[k].endsWith(']')) {
|
||||||
|
row[k] = row[k].substring(1, row[k].length - 1);
|
||||||
|
}
|
||||||
|
const tags = row[k].split(',');
|
||||||
_credential.tags = tags.map(tagMapper);
|
_credential.tags = tags.map(tagMapper);
|
||||||
}
|
}
|
||||||
|
} else if(field === 'compromised'){
|
||||||
|
_credential[field] = (row[k] === 'true' || row[k] === '1');
|
||||||
|
} else if (field === 'created' || field === 'changed' || field === 'expire_time' || field === 'delete_time') {
|
||||||
|
const num = parseInt(row[k]);
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
_credential[field] = num;
|
||||||
|
}
|
||||||
} else{
|
} else{
|
||||||
_credential[field] = row[k];
|
_credential[field] = row[k];
|
||||||
}
|
}
|
||||||
|
@ -181,19 +229,19 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$scope.inspectCredential = function (row) {
|
$scope.inspectCredential = async function (row) {
|
||||||
$scope.inspected_credential = rowToCredential(row);
|
$scope.inspected_credential = await rowToCredential(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.csvLoaded = function (file) {
|
$scope.csvLoaded = function (file) {
|
||||||
$scope.import_fields = [];
|
$scope.import_fields = [];
|
||||||
$scope.inspected_credential = false;
|
$scope.inspected_credential = {};
|
||||||
$scope.matched = false;
|
$scope.atLeastlabelMatched = false;
|
||||||
var file_data = file.data.split(',');
|
var file_data = file.data.split(',');
|
||||||
file_data = decodeURIComponent(escape(window.atob(file_data[1])));
|
file_data = decodeURIComponent(escape(window.atob(file_data[1])));
|
||||||
/** global: Papa */
|
/** global: Papa */
|
||||||
Papa.parse(file_data, {
|
Papa.parse(file_data, {
|
||||||
complete: function(results) {
|
complete: async function(results) {
|
||||||
if(results.data) {
|
if(results.data) {
|
||||||
for(var i = 0; i < results.data[0].length; i++){
|
for(var i = 0; i < results.data[0].length; i++){
|
||||||
var propName = results.data[0][i];
|
var propName = results.data[0][i];
|
||||||
|
@ -203,13 +251,15 @@
|
||||||
if(credentialProperty.matching){
|
if(credentialProperty.matching){
|
||||||
if(credentialProperty.matching.indexOf(propName.toLowerCase()) !== -1){
|
if(credentialProperty.matching.indexOf(propName.toLowerCase()) !== -1){
|
||||||
$scope.import_fields[i] = credentialProperty.prop;
|
$scope.import_fields[i] = credentialProperty.prop;
|
||||||
$scope.matched = true;
|
if (credentialProperty.prop === 'label') {
|
||||||
|
$scope.atLeastlabelMatched = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if($scope.matched){
|
if($scope.atLeastlabelMatched){
|
||||||
$scope.inspectCredential(results.data[1]);
|
await $scope.inspectCredential(results.data[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var j = 0; j < results.data.length; j++){
|
for(var j = 0; j < results.data.length; j++){
|
||||||
|
@ -242,6 +292,7 @@
|
||||||
};
|
};
|
||||||
$scope.log.push($translate.instant('done'));
|
$scope.log.push($translate.instant('done'));
|
||||||
$scope.importing = false;
|
$scope.importing = false;
|
||||||
|
$rootScope.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,5 +325,12 @@
|
||||||
var start = ($scope.skipFirstRow) ? 1 : 0;
|
var start = ($scope.skipFirstRow) ? 1 : 0;
|
||||||
$scope.inspectCredential($scope.parsed_csv[start]);
|
$scope.inspectCredential($scope.parsed_csv[start]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.fileLoadError = function (file) {
|
||||||
|
console.error($translate.instant('error.loading.file'), file);
|
||||||
|
};
|
||||||
|
$scope.fileSelectProgress = function () {
|
||||||
|
|
||||||
|
};
|
||||||
}]);
|
}]);
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
replace: true,
|
replace: true,
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
scope: {
|
scope: {
|
||||||
credential: '=credentialTemplate'
|
credential: '='
|
||||||
},
|
},
|
||||||
|
|
||||||
link: function (scope, element, attrs) {
|
link: function (scope, element, attrs) {
|
||||||
|
|
|
@ -34,33 +34,50 @@ PassmanExporter.csv.export = function (credentials, FileService, EncryptService,
|
||||||
/** global: C_Promise */
|
/** global: C_Promise */
|
||||||
return new C_Promise(function () {
|
return new C_Promise(function () {
|
||||||
PassmanExporter.getCredentialsWithFiles(credentials, FileService, EncryptService, _log, $translate).then((function(){
|
PassmanExporter.getCredentialsWithFiles(credentials, FileService, EncryptService, _log, $translate).then((function(){
|
||||||
var headers = ['label', 'username', 'password', 'email', 'description', 'tags', 'url', 'custom_fields', 'files'];
|
const headers = [
|
||||||
var file_data = '"' + headers.join('","') + '"\n';
|
'label',
|
||||||
for (var i = 0; i < credentials.length; i++) {
|
'description',
|
||||||
var _credential = credentials[i];
|
'created',
|
||||||
var row_data = [];
|
'changed',
|
||||||
for (var h = 0; h < headers.length; h++) {
|
'tags',
|
||||||
var field = headers[h];
|
'email',
|
||||||
|
'icon',
|
||||||
|
'username',
|
||||||
|
'password',
|
||||||
|
'url',
|
||||||
|
'renew_interval',
|
||||||
|
'expire_time',
|
||||||
|
'delete_time',
|
||||||
|
'files',
|
||||||
|
'custom_fields',
|
||||||
|
'otp',
|
||||||
|
'compromised',
|
||||||
|
'hidden'
|
||||||
|
];
|
||||||
|
let file_data = '"' + headers.join('","') + '"\n';
|
||||||
|
for (let i = 0; i < credentials.length; i++) {
|
||||||
|
const _credential = credentials[i];
|
||||||
|
let row_data = [];
|
||||||
|
for (const field of headers) {
|
||||||
if (field === 'tags') {
|
if (field === 'tags') {
|
||||||
var _tags = [];
|
let _tags = [];
|
||||||
for (var t = 0; t < _credential[field].length; t++) {
|
for (const tag_field of _credential[field]) {
|
||||||
_tags.push(_credential[field][t].text);
|
_tags.push(tag_field.text);
|
||||||
}
|
}
|
||||||
var tag_data = '[' + _tags.join(",") + ']';
|
const tag_data = '[' + _tags.join(",") + ']';
|
||||||
row_data.push('"' + tag_data.replaceAll('"', '""') + '"');
|
row_data.push('"' + tag_data.replaceAll('"', '""') + '"');
|
||||||
}
|
} else if (field === 'custom_fields' || field === 'files' || field === 'otp' || field === 'icon') {
|
||||||
else if (field == 'custom_fields' || field == 'files') {
|
let _fields = JSON.stringify(_credential[field]);
|
||||||
var _fields = JSON.stringify(_credential[field]);
|
_fields = _fields.replaceAll('"', '""');
|
||||||
_fields = _fields.replaceAll('"', '""');
|
row_data.push('"' + _fields + '"');
|
||||||
row_data.push('"' + _fields + '"');
|
} else {
|
||||||
}
|
let data = _credential[field];
|
||||||
else {
|
data = typeof data === 'number' || typeof data === 'boolean' ? "" + data : data;
|
||||||
var data = _credential[field],
|
const value = (data === null || data === undefined) ? '' : data.replaceAll('"', '""');
|
||||||
value = data === null ? '':data.replaceAll('"', '""');
|
row_data.push('"' + value + '"');
|
||||||
row_data.push('"' + value + '"');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var progress = {
|
let progress = {
|
||||||
percent: i / credentials.length * 100,
|
percent: i / credentials.length * 100,
|
||||||
loaded: i,
|
loaded: i,
|
||||||
total: credentials.length
|
total: credentials.length
|
||||||
|
|
|
@ -34,9 +34,9 @@ PassmanExporter.json.export = function (credentials, FileService, EncryptService
|
||||||
/** global: C_Promise */
|
/** global: C_Promise */
|
||||||
return new C_Promise(function () {
|
return new C_Promise(function () {
|
||||||
PassmanExporter.getCredentialsWithFiles(credentials, FileService, EncryptService, _log, $translate).then((function(){
|
PassmanExporter.getCredentialsWithFiles(credentials, FileService, EncryptService, _log, $translate).then((function(){
|
||||||
var _output = [];
|
let _output = [];
|
||||||
for (var i = 0; i < credentials.length; i++) {
|
for (let i = 0; i < credentials.length; i++) {
|
||||||
var _credential = angular.copy(credentials[i]);
|
let _credential = angular.copy(credentials[i]);
|
||||||
|
|
||||||
delete _credential.vault_key;
|
delete _credential.vault_key;
|
||||||
delete _credential.vault_id;
|
delete _credential.vault_id;
|
||||||
|
@ -44,14 +44,14 @@ PassmanExporter.json.export = function (credentials, FileService, EncryptService
|
||||||
|
|
||||||
_output.push(_credential);
|
_output.push(_credential);
|
||||||
|
|
||||||
var progress = {
|
let progress = {
|
||||||
percent: i / credentials.length * 100,
|
percent: i / credentials.length * 100,
|
||||||
loaded: i,
|
loaded: i,
|
||||||
total: credentials.length
|
total: credentials.length
|
||||||
};
|
};
|
||||||
this.call_progress(progress);
|
this.call_progress(progress);
|
||||||
}
|
}
|
||||||
var file_data = JSON.stringify(_output);
|
let file_data = JSON.stringify(_output);
|
||||||
this.call_then();
|
this.call_then();
|
||||||
download(file_data, 'passman-export.json');
|
download(file_data, 'passman-export.json');
|
||||||
}).bind(this)).progress(function() {
|
}).bind(this)).progress(function() {
|
||||||
|
|
|
@ -75,44 +75,49 @@ if (!window['PassmanExporter']) {
|
||||||
}
|
}
|
||||||
}).bind(this);
|
}).bind(this);
|
||||||
|
|
||||||
for (var i = 0; i < credentials.length; i++) {
|
for (let i = 0; i < credentials.length; i++) {
|
||||||
|
const credential = credentials[i];
|
||||||
var item = credentials[i];
|
|
||||||
|
|
||||||
// Custom fields
|
// Custom fields
|
||||||
for (c = 0; c < item.custom_fields.length; c++) {
|
for (let c = 0; c < credential.custom_fields.length; c++) {
|
||||||
var cf = item.custom_fields[c];
|
const cf = credential.custom_fields[c];
|
||||||
if (cf.field_type === 'file') {
|
if (cf.field_type === 'file') {
|
||||||
|
const file = cf.value;
|
||||||
|
if (file !== "undefined" && file !== undefined && file !== null && file.file_id !== undefined) {
|
||||||
|
this.parent.total++;
|
||||||
|
this.parent.fileGUID_cred[file.guid] = {
|
||||||
|
cred_pos: i,
|
||||||
|
on: 'custom_fields',
|
||||||
|
at: c
|
||||||
|
};
|
||||||
|
|
||||||
|
this.parent.FS.getFile(file).then((function (data) {
|
||||||
|
this.parent.step(data);
|
||||||
|
}).bind(this), (function (error) {
|
||||||
|
this.parent.stepFailed(error);
|
||||||
|
}).bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also get all files
|
||||||
|
for (let c = 0; c < credential.files.length; c++) {
|
||||||
|
const file = credential.files[c];
|
||||||
|
if (file !== "undefined" && file !== undefined && file !== null && file.file_id !== undefined) {
|
||||||
this.parent.total++;
|
this.parent.total++;
|
||||||
this.parent.fileGUID_cred[cf.value.guid] = {
|
this.parent.fileGUID_cred[file.guid] = {
|
||||||
cred_pos: i,
|
cred_pos: i,
|
||||||
on: 'custom_fields',
|
on: 'files',
|
||||||
at: c
|
at: c
|
||||||
};
|
};
|
||||||
|
|
||||||
this.parent.FS.getFile(cf.value).then((function (data) {
|
this.parent.FS.getFile(file).then((function (data) {
|
||||||
this.parent.step(data);
|
this.parent.step(data);
|
||||||
}).bind(this), (function (error) {
|
}).bind(this), (function (error) {
|
||||||
this.parent.stepFailed(error);
|
this.parent.stepFailed(error);
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also get all files
|
|
||||||
for (var c = 0; c < item.files.length; c++) {
|
|
||||||
this.parent.total++;
|
|
||||||
this.parent.fileGUID_cred[item.files[c].guid] = {
|
|
||||||
cred_pos: i,
|
|
||||||
on: 'files',
|
|
||||||
at: c
|
|
||||||
};
|
|
||||||
|
|
||||||
this.parent.FS.getFile(item.files[c]).then((function (data) {
|
|
||||||
this.parent.step(data);
|
|
||||||
}).bind(this), (function (error) {
|
|
||||||
this.parent.stepFailed(error);
|
|
||||||
}).bind(this));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have finished downloading everything, so let's hand over job to somewhere else!
|
// We have finished downloading everything, so let's hand over job to somewhere else!
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -98,10 +98,10 @@ h3 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setting-group.margin-bottom-25 {
|
.setting-group.margin-bottom-25, .margin-bottom-25 {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
.setting-group.margin-bottom-10 {
|
.setting-group.margin-bottom-10, .margin-bottom-10 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.display-grid {
|
.display-grid {
|
||||||
|
@ -117,7 +117,7 @@ h3 {
|
||||||
label[for=confirmVaultPWChange] {
|
label[for=confirmVaultPWChange] {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
label[for=confirmVaultDelete] {
|
label[for=confirmVaultDelete], label[for=skipFirstRow] {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<div ng-controller="GenericCsvImportCtrl">
|
<div ng-controller="GenericCsvImportCtrl">
|
||||||
<div class="row">
|
<div class="row margin-bottom-25">
|
||||||
<div class="col-xs-12 col-md-3">
|
<div class="col-xs-12 col-md-3">
|
||||||
<div>{{ 'select.csv' | translate}}
|
<div>{{ 'select.csv' | translate}}
|
||||||
<input type="file" file-select accept=".csv"
|
<input type="file" file-select accept=".csv"
|
||||||
success="csvLoaded">
|
success="csvLoaded" error="fileLoadError" progress="fileSelectProgress">
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="parsed_csv">
|
<div ng-show="parsed_csv">
|
||||||
<span translate="parsed.csv.rows" translate-value-rows="{{ parsed_csv.length }}">
|
<span translate="parsed.csv.rows" translate-value-rows="{{ parsed_csv.length }}">
|
||||||
|
@ -11,7 +11,8 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="parsed_csv">
|
<div ng-show="parsed_csv">
|
||||||
<input type="checkbox" ng-model="skipFirstRow"> {{ 'skip.first.row' | translate}}
|
<input id="skipFirstRow" class="checkbox" type="checkbox" ng-model="skipFirstRow">
|
||||||
|
<label for="skipFirstRow">{{'skip.first.row' | translate}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="import_fields.indexOf('label') === -1 && parsed_csv">
|
<div ng-show="import_fields.indexOf('label') === -1 && parsed_csv">
|
||||||
<b>{{ 'import.csv.label.req' | translate}}</b>
|
<b>{{ 'import.csv.label.req' | translate}}</b>
|
||||||
|
@ -25,40 +26,42 @@
|
||||||
<div progress-bar="import_progress.progress" index="import_progress.loaded" total="import_progress.total"></div>
|
<div progress-bar="import_progress.progress" index="import_progress.loaded" total="import_progress.total"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<div ng-if="log" class="import_log">
|
|
||||||
<textarea id="import_log" auto-scroll="log">{{log.join('\n')}}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-9" ng-show="parsed_csv">
|
<div class="col-xs-12 col-md-9">
|
||||||
|
<div ng-if="log" class="import_log">
|
||||||
|
<textarea id="import_log" auto-scroll="log">{{log.join('\n')}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 display-grid" ng-show="parsed_csv">
|
||||||
<b>{{ 'first.five.lines' | translate }}</b><br />
|
<b>{{ 'first.five.lines' | translate }}</b><br />
|
||||||
{{ 'assign.column' | translate }}
|
{{ 'assign.column' | translate }}
|
||||||
<div class="import-table-outter">
|
<div class="import-table-outter margin-bottom-25">
|
||||||
<table class="import-table">
|
<table class="import-table">
|
||||||
<tr ng-repeat="line in parsed_csv | limitTo:5">
|
<tr ng-repeat="line in parsed_csv | limitTo:5">
|
||||||
<td class="inspect"><i class="fa fa-search"
|
<td class="inspect"><i class="fa fa-search"
|
||||||
ng-click="inspectCredential(line)"
|
ng-click="inspectCredential(line)"
|
||||||
ng-if="($index > 0 && matched && import_fields.length > 0) || ($index >= 0 && !matched && import_fields.length > 0)"></i>
|
ng-if="($index > 0 && matched && import_fields.length > 0) || ($index >= 0 && !matched && import_fields.length > 0)"></i>
|
||||||
</td>
|
</td>
|
||||||
<td ng-repeat="prop in line track by $index">
|
<td ng-repeat="prop in line track by $index">
|
||||||
{{line[$index]}}
|
{{"" + prop | limitTo: 100}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="line in parsed_csv | limitTo:1">
|
<tr ng-repeat="line in parsed_csv | limitTo:1">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td ng-repeat="prop in line track by $index">
|
<td ng-repeat="prop in line track by $index">
|
||||||
<select ng-model="import_fields[$index]" ng-change="updateExample()"
|
<select ng-model="import_fields[$index]" ng-change="updateExample()"
|
||||||
ng-options="property.prop as property.label for property in credentialProperties">
|
ng-options="property.prop as property.label for property in credentialProperties">
|
||||||
|
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="inspected_credential && import_fields.length > 0">
|
<div ng-show="inspected_credential && import_fields.length > 0">
|
||||||
<b>{{ 'example.credential' | translate}}</b>
|
<b>{{ 'example.credential' | translate}}</b>
|
||||||
<div credential-template="inspected_credential" show-label>
|
<div credential-template credential="inspected_credential" show-label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
<h2 class="sidebar-label">{{selectedCredential.label}}</h2>
|
<h2 class="sidebar-label">{{selectedCredential.label}}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div credential-template="selectedCredential"></div>
|
<div credential-template credential="selectedCredential"></div>
|
||||||
|
|
||||||
<div ng-show="selectedCredential">
|
<div ng-show="selectedCredential">
|
||||||
<div>
|
<div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue