autofill ui for other types. autofill fixes

This commit is contained in:
Kyle Spearrin 2017-10-17 15:22:16 -04:00
parent cebf913999
commit e87f9e268a
13 changed files with 130 additions and 55 deletions

View File

@ -242,8 +242,8 @@
"edit": { "edit": {
"message": "Edit" "message": "Edit"
}, },
"noLoginsInList": { "noItemsInList": {
"message": "There are no logins to list." "message": "There are no items to list."
}, },
"loginInformation": { "loginInformation": {
"message": "Login Information" "message": "Login Information"
@ -931,6 +931,9 @@
"typeLogin": { "typeLogin": {
"message": "Login" "message": "Login"
}, },
"typeLogins": {
"message": "Logins"
},
"typeSecureNote": { "typeSecureNote": {
"message": "Secure Note" "message": "Secure Note"
}, },

View File

@ -736,7 +736,7 @@ var bg_isBackground = true,
function autofillPage() { function autofillPage() {
bg_autofillService.doAutoFill({ bg_autofillService.doAutoFill({
login: loginToAutoFill, cipher: loginToAutoFill,
pageDetails: pageDetailsToAutoFill, pageDetails: pageDetailsToAutoFill,
fromBackground: true fromBackground: true
}); });

View File

@ -0,0 +1,27 @@
angular
.module('bit.components')
.component('cipherItems', {
bindings: {
ciphers: '<',
selectionTitle: '<',
onView: '&',
onSelected: '&'
},
templateUrl: 'app/components/views/cipherItems.html',
controller: function (i18nService) {
var ctrl = this;
ctrl.$onInit = function () {
ctrl.i18n = i18nService;
ctrl.view = function (cipher) {
ctrl.onView()(cipher);
};
ctrl.select = function (cipher) {
ctrl.onSelected()(cipher);
};
};
}
});

View File

@ -1,7 +1,7 @@
<div class="action-buttons"> <div class="action-buttons">
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.login"> <div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.login">
<span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.launchWebsite}}" ng-click="$ctrl.launch()" <span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.launchWebsite}}" ng-click="$ctrl.launch()"
ng-if="$ctrl.cipher.login.uri && !$ctrl.showView"> ng-if="!$ctrl.showView" ng-class="{disabled: !$ctrl.cipher.login.uri}">
<i class="fa fa-lg fa-share-square-o"></i> <i class="fa fa-lg fa-share-square-o"></i>
</span> </span>
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{i18n.view}}" <span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{i18n.view}}"
@ -11,28 +11,38 @@
<span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyUsername}}" ngclipboard <span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyUsername}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)" ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.username, 'Username')" ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.username, 'Username')"
data-clipboard-text="{{$ctrl.cipher.login.username}}" ng-if="$ctrl.cipher.login.username"> data-clipboard-text="{{$ctrl.cipher.login.username}}" ng-class="{disabled: !$ctrl.cipher.login.username}">
<i class="fa fa-lg fa-user"></i> <i class="fa fa-lg fa-user"></i>
</span> </span>
<span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyPassword}}" ngclipboard <span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyPassword}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)" ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.password, 'Password')" ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.password, 'Password')"
data-clipboard-text="{{$ctrl.cipher.login.password}}" ng-if="$ctrl.cipher.login.password"> data-clipboard-text="{{$ctrl.cipher.login.password}}" ng-class="{disabled: !$ctrl.cipher.login.password}">
<i class="fa fa-lg fa-key"></i> <i class="fa fa-lg fa-key"></i>
</span> </span>
</div> </div>
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.card"> <div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.card">
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{i18n.view}}"
ng-if="$ctrl.showView">
<i class="fa fa-lg fa-eye"></i>
</span>
<span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyNumber}}" ngclipboard <span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyNumber}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)" ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.number, 'Card Number')" ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.number, 'Card Number')"
data-clipboard-text="{{$ctrl.cipher.card.number}}" ng-if="$ctrl.cipher.card.number"> data-clipboard-text="{{$ctrl.cipher.card.number}}" ng-class="{disabled: !$ctrl.cipher.card.number}">
<i class="fa fa-lg fa-hashtag"></i> <i class="fa fa-lg fa-hashtag"></i>
</span> </span>
<span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copySecurityCode}}" ngclipboard <span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copySecurityCode}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)" ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.securityCode, 'Security Code')" ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.securityCode, 'Security Code')"
data-clipboard-text="{{$ctrl.cipher.card.code}}" ng-if="$ctrl.cipher.card.code"> data-clipboard-text="{{$ctrl.cipher.card.code}}" ng-class="{disabled: !$ctrl.cipher.card.code}">
<i class="fa fa-lg fa-key"></i> <i class="fa fa-lg fa-key"></i>
</span> </span>
</div> </div>
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.identity">
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{i18n.view}}"
ng-if="$ctrl.showView">
<i class="fa fa-lg fa-eye"></i>
</span>
</div>
</div> </div>

View File

@ -0,0 +1,11 @@
<a href="#" stop-click ng-click="$ctrl.select(cipher)" class="list-section-item condensed"
title="{{$ctrl.selectionTitle}}" ng-repeat="cipher in $ctrl.ciphers track by $index">
<action-buttons cipher="cipher" show-view="true" on-view="$ctrl.view(cipher)"></action-buttons>
<icon cipher="cipher"></icon>
<span class="text">
{{cipher.name}}
<i class="fa fa-share-alt text-muted" ng-if="cipher.organizationId" title="{{$ctrl.i18n.shared}}"></i>
<i class="fa fa-paperclip text-muted" ng-if="cipher.attachments" title="{{$ctrl.i18n.attachments}}"></i>
</span>
<span class="detail">{{cipher.subTitle}}</span>
</a>

View File

@ -2,7 +2,7 @@ angular
.module('bit.current') .module('bit.current')
.controller('currentController', function ($scope, cipherService, utilsService, toastr, $window, $state, $timeout, .controller('currentController', function ($scope, cipherService, utilsService, toastr, $window, $state, $timeout,
autofillService, $analytics, i18nService, totpService, tokenService) { autofillService, $analytics, i18nService, totpService, tokenService, constantsService, $filter) {
$scope.i18n = i18nService; $scope.i18n = i18nService;
var pageDetails = [], var pageDetails = [],
@ -10,7 +10,8 @@ angular
domain = null, domain = null,
canAutofill = false; canAutofill = false;
$scope.ciphers = []; $scope.loginCiphers = [];
$scope.otherCiphers = [];
$scope.loaded = false; $scope.loaded = false;
$scope.searchText = null; $scope.searchText = null;
$('#search').focus(); $('#search').focus();
@ -44,10 +45,24 @@ angular
canAutofill = true; canAutofill = true;
}); });
cipherService.getAllDecryptedForDomain(domain).then(function (ciphers) { var otherTypes = [constantsService.cipherType.card, constantsService.cipherType.identity];
cipherService.getAllDecryptedForDomain(domain, otherTypes).then(function (ciphers) {
var loginCiphers = [],
otherCiphers = [];
for (var i = 0; i < ciphers.length; i++) {
if (ciphers[i].type === constantsService.cipherType.login) {
loginCiphers.push(ciphers[i]);
}
else {
otherCiphers.push(ciphers[i]);
}
}
$timeout(function () { $timeout(function () {
$scope.loginCiphers = $filter('orderBy')(loginCiphers, [sortUriMatch, sortLastUsed, 'name', 'subTitle']);
$scope.otherCiphers = $filter('orderBy')(otherCiphers, [sortLastUsed, 'name', 'subTitle']);
$scope.loaded = true; $scope.loaded = true;
$scope.ciphers = ciphers;
}); });
}); });
}); });
@ -94,14 +109,14 @@ angular
}); });
}; };
$scope.sortUriMatch = function (cipher) { function sortUriMatch(cipher) {
// exact matches should sort earlier. // exact matches should sort earlier.
return url && url.startsWith(cipher.uri) ? 0 : 1; return url && url.startsWith(cipher.uri) ? 0 : 1;
}; }
$scope.sortLastUsed = function (cipher) { function sortLastUsed(cipher) {
return cipher.localData && cipher.localData.lastUsedDate ? -1 * cipher.localData.lastUsedDate : 0; return cipher.localData && cipher.localData.lastUsedDate ? -1 * cipher.localData.lastUsedDate : 0;
}; }
$scope.searchVault = function () { $scope.searchVault = function () {
$state.go('tabs.vault', { $state.go('tabs.vault', {

View File

@ -14,29 +14,30 @@
</div> </div>
</div> </div>
<div class="content content-tabs"> <div class="content content-tabs">
<div ng-if="ciphers.length"> <div class="list" ng-if="loaded">
<div class="list"> <div class="list-section">
<div class="list-grouped"> <div class="list-section-header">
<a href="#" stop-click ng-click="fillCipher(cipher)" class="list-grouped-item condensed" {{i18n.typeLogins}}
title="{{i18n.autoFill}} {{cipher.name}}" </div>
ng-repeat="cipher in theCiphers = (ciphers | orderBy: [sortUriMatch, sortLastUsed, 'name', 'subTitle']) <div class="list-section-items" ng-class="{'list-no-selection': !loginCiphers.length}">
track by $index"> <div class="list-section-item" ng-if="loaded && !loginCiphers.length">
<action-buttons cipher="cipher" show-view="true" on-view="viewCipher(cipher)"></action-buttons> <p>{{i18n.autoFillInfo}}</p>
<icon cipher="cipher"></icon> <button ng-click="addCipher()" class="btn btn-link btn-block">{{i18n.addLogin}}</button>
<span class="text"> </div>
{{cipher.name}} <cipher-items ng-if="loginCiphers.length" ciphers="loginCiphers" on-view="viewCipher"
<i class="fa fa-share-alt text-muted" ng-if="cipher.organizationId" title="{{i18n.shared}}"></i> on-selected="fillCipher" selection-title="i18n.autoFill"></cipher-items>
</span> </div>
<span class="detail">{{cipher.subTitle}}</span> </div>
</a> <div class="list-section" ng-class="{'list-no-selection': !otherCiphers.length}">
<div class="list-section-header">
{{i18n.other}}
</div>
<div class="list-section-items">
<div class="list-section-item" ng-if="loaded && !otherCiphers.length">{{i18n.noItemsInList}}</div>
<cipher-items ng-if="otherCiphers.length" ciphers="otherCiphers" on-view="viewCipher"
on-selected="fillCipher" selection-title="i18n.autoFill"></cipher-items>
</div> </div>
</div> </div>
</div>
<div class="centered-message" ng-if="loaded && !ciphers.length">
<p>
{{i18n.autoFillInfo}}
<button ng-click="addCipher()" style="margin-top: 20px;" class="btn btn-link btn-block">{{i18n.addLogin}}</button>
</p>
</div> </div>
<div class="page-loading" ng-if="!loaded"> <div class="page-loading" ng-if="!loaded">
<i class="fa fa-lg fa-spinner fa-spin"></i> <i class="fa fa-lg fa-spinner fa-spin"></i>

View File

@ -75,7 +75,7 @@
</div> </div>
<div class="centered-message" ng-if="loaded && !vaultCiphers.length"> <div class="centered-message" ng-if="loaded && !vaultCiphers.length">
<p> <p>
{{i18n.noLoginsInList}} {{i18n.noItemsInList}}
<button ng-click="addCipher()" style="margin-top: 20px;" class="btn btn-link btn-block">{{i18n.addLogin}}</button> <button ng-click="addCipher()" style="margin-top: 20px;" class="btn btn-link btn-block">{{i18n.addLogin}}</button>
</p> </p>
</div> </div>

View File

@ -36,7 +36,7 @@
</div> </div>
<div class="centered-message" ng-if="loaded && !vaultCiphers.length"> <div class="centered-message" ng-if="loaded && !vaultCiphers.length">
<p> <p>
{{i18n.noLoginsInList}} {{i18n.noItemsInList}}
<button ng-click="addCipher()" style="margin-top: 20px;" class="btn btn-link btn-block">{{i18n.addLogin}}</button> <button ng-click="addCipher()" style="margin-top: 20px;" class="btn btn-link btn-block">{{i18n.addLogin}}</button>
</p> </p>
</div> </div>

View File

@ -63,6 +63,7 @@
<script src="app/components/componentsModule.js"></script> <script src="app/components/componentsModule.js"></script>
<script src="app/components/iconComponent.js"></script> <script src="app/components/iconComponent.js"></script>
<script src="app/components/actionButtonsComponent.js"></script> <script src="app/components/actionButtonsComponent.js"></script>
<script src="app/components/cipherItemsComponent.js"></script>
<script src="app/services/servicesModule.js"></script> <script src="app/services/servicesModule.js"></script>
<script src="app/services/backgroundService.js"></script> <script src="app/services/backgroundService.js"></script>

View File

@ -510,12 +510,12 @@
} }
} }
} }
}
&.list-no-selection { .list-no-selection {
.list-grouped-item:not(.list-allow-selection), .list-section-item:not(.list-allow-selection) { .list-grouped-item:not(.list-allow-selection), .list-section-item:not(.list-allow-selection) {
&:hover { &:hover {
background-color: white; background-color: white;
}
} }
} }
} }

View File

@ -201,7 +201,7 @@ function initAutofill() {
return; return;
} }
if (!tab || !options.login || !options.pageDetails || !options.pageDetails.length) { if (!tab || !options.cipher || !options.pageDetails || !options.pageDetails.length) {
deferred.reject(); deferred.reject();
return; return;
} }
@ -213,20 +213,24 @@ function initAutofill() {
continue; continue;
} }
var fillScript = self.generateFillScript(options.pageDetails[i].details, { var fillOptions = {
username: options.login.username, fields: options.cipher.fields,
password: options.login.password,
fields: options.login.fields,
skipUsernameOnlyFill: options.skipUsernameOnlyFill || false skipUsernameOnlyFill: options.skipUsernameOnlyFill || false
}); };
if (options.cipher.login) {
fillOptions.username = options.cipher.login.username;
fillOptions.password = options.cipher.login.password;
}
var fillScript = self.generateFillScript(options.pageDetails[i].details, fillOptions);
if (!fillScript || !fillScript.script || !fillScript.script.length) { if (!fillScript || !fillScript.script || !fillScript.script.length) {
continue; continue;
} }
didAutofill = true; didAutofill = true;
if (!options.skipLastUsed) { if (!options.skipLastUsed) {
self.cipherService.updateLastUsedDate(options.login.id); self.cipherService.updateLastUsedDate(options.cipher.id);
} }
chrome.tabs.sendMessage(tab.id, { chrome.tabs.sendMessage(tab.id, {
@ -235,14 +239,14 @@ function initAutofill() {
}, { frameId: options.pageDetails[i].frameId }); }, { frameId: options.pageDetails[i].frameId });
if (totpPromise || (options.fromBackground && self.utilsService.isFirefox()) || if (totpPromise || (options.fromBackground && self.utilsService.isFirefox()) ||
options.skipTotp || !options.login.totp || !self.tokenService.getPremium()) { options.skipTotp || !options.cipher.login || !options.cipher.login.totp || !self.tokenService.getPremium()) {
continue; continue;
} }
totpPromise = self.totpService.isAutoCopyEnabled().then(function (enabled) { totpPromise = self.totpService.isAutoCopyEnabled().then(function (enabled) {
if (enabled) { if (enabled) {
/* jshint ignore:start */ /* jshint ignore:start */
return self.totpService.getCode(options.login.totp); return self.totpService.getCode(options.cipher.login.totp);
/* jshint ignore:end */ /* jshint ignore:end */
} }
@ -300,7 +304,7 @@ function initAutofill() {
} }
self.doAutoFill({ self.doAutoFill({
login: cipher, cipher: cipher,
pageDetails: pageDetails, pageDetails: pageDetails,
fromBackground: true, fromBackground: true,
skipTotp: !fromCommand, skipTotp: !fromCommand,

View File

@ -260,7 +260,7 @@ function initCipherService() {
}); });
}; };
CipherService.prototype.getAllDecryptedForDomain = function (domain) { CipherService.prototype.getAllDecryptedForDomain = function (domain, includeOtherTypes) {
var self = this; var self = this;
var eqDomainsPromise = self.settingsService.getEquivalentDomains().then(function (eqDomains) { var eqDomainsPromise = self.settingsService.getEquivalentDomains().then(function (eqDomains) {
@ -288,6 +288,9 @@ function initCipherService() {
matchingDomains.indexOf(ciphers[i].login.domain) > -1) { matchingDomains.indexOf(ciphers[i].login.domain) > -1) {
ciphersToReturn.push(ciphers[i]); ciphersToReturn.push(ciphers[i]);
} }
else if (includeOtherTypes && includeOtherTypes.indexOf(ciphers[i].type) > -1) {
ciphersToReturn.push(ciphers[i]);
}
} }
return ciphersToReturn; return ciphersToReturn;