convert edit to generic ciphers

This commit is contained in:
Kyle Spearrin 2017-10-06 21:24:04 -04:00
parent d28c59544f
commit c4d2045884
6 changed files with 309 additions and 235 deletions

View File

@ -28,6 +28,17 @@ angular.module('bit')
email: 1, email: 1,
remember: 5 remember: 5
}, },
cipherType: {
login: 1,
secureNote: 2,
card: 3,
identity: 4
},
fieldType: {
text: 0,
hidden: 1,
boolean: 2
},
twoFactorProviderInfo: [ twoFactorProviderInfo: [
{ {
type: 0, type: 0,

View File

@ -137,32 +137,32 @@
} }
}; };
$scope.editLogin = function (login) { $scope.editLogin = function (cipher) {
var editModel = $uibModal.open({ var editModel = $uibModal.open({
animation: true, animation: true,
templateUrl: 'app/vault/views/vaultEditLogin.html', templateUrl: 'app/vault/views/vaultEditCipher.html',
controller: 'vaultEditLoginController', controller: 'vaultEditCipherController',
resolve: { resolve: {
loginId: function () { return login.id; } cipherId: function () { return cipher.id; }
} }
}); });
editModel.result.then(function (returnVal) { editModel.result.then(function (returnVal) {
if (returnVal.action === 'edit') { if (returnVal.action === 'edit') {
login.folderId = returnVal.data.folderId; cipher.folderId = returnVal.data.folderId;
login.name = returnVal.data.name; cipher.name = returnVal.data.name;
login.username = returnVal.data.username; cipher.username = returnVal.data.login.username;
login.password = returnVal.data.password; cipher.password = returnVal.data.login.password;
login.favorite = returnVal.data.favorite; cipher.favorite = returnVal.data.favorite;
sortScopedLoginData(); sortScopedLoginData();
} }
else if (returnVal.action === 'partialEdit') { else if (returnVal.action === 'partialEdit') {
login.folderId = returnVal.data.folderId; cipher.folderId = returnVal.data.folderId;
login.favorite = returnVal.data.favorite; cipher.favorite = returnVal.data.favorite;
} }
else if (returnVal.action === 'delete') { else if (returnVal.action === 'delete') {
removeLoginFromScopes(login); removeLoginFromScopes(cipher);
} }
}); });
}; };

View File

@ -1,33 +1,34 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultEditLoginController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService, .controller('vaultEditCipherController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService,
passwordService, loginId, $analytics, $rootScope, authService, $uibModal) { passwordService, cipherId, $analytics, $rootScope, authService, $uibModal, constants) {
$analytics.eventTrack('vaultEditLoginController', { category: 'Modal' }); $analytics.eventTrack('vaultEditCipherController', { category: 'Modal' });
$scope.folders = $rootScope.vaultFolders; $scope.folders = $rootScope.vaultFolders;
$scope.login = {}; $scope.cipher = {};
$scope.readOnly = false; $scope.readOnly = false;
$scope.constants = constants;
authService.getUserProfile().then(function (profile) { authService.getUserProfile().then(function (profile) {
$scope.useTotp = profile.premium; $scope.useTotp = profile.premium;
return apiService.ciphers.get({ id: loginId }).$promise; return apiService.ciphers.get({ id: cipherId }).$promise;
}).then(function (login) { }).then(function (cipher) {
$scope.login = cipherService.decryptLogin(login); $scope.cipher = cipherService.decryptCipher(cipher);
$scope.readOnly = !$scope.login.edit; $scope.readOnly = !$scope.cipher.edit;
$scope.useTotp = $scope.useTotp || $scope.login.organizationUseTotp; $scope.useTotp = $scope.useTotp || $scope.cipher.organizationUseTotp;
}); });
$scope.save = function (model) { $scope.save = function (model) {
if ($scope.readOnly) { if ($scope.readOnly) {
$scope.savePromise = apiService.ciphers.putPartial({ id: loginId }, { $scope.savePromise = apiService.ciphers.putPartial({ id: cipherId }, {
folderId: model.folderId, folderId: model.folderId,
favorite: model.favorite favorite: model.favorite
}, function (response) { }, function (response) {
$analytics.eventTrack('Partially Edited Login'); $analytics.eventTrack('Partially Edited Cipher');
$uibModalInstance.close({ $uibModalInstance.close({
action: 'partialEdit', action: 'partialEdit',
data: { data: {
id: loginId, id: cipherId,
favorite: model.favorite, favorite: model.favorite,
folderId: model.folderId && model.folderId !== '' ? model.folderId : null folderId: model.folderId && model.folderId !== '' ? model.folderId : null
} }
@ -35,46 +36,46 @@
}).$promise; }).$promise;
} }
else { else {
var login = cipherService.encryptLogin(model); var cipher = cipherService.encryptCipher(model, $scope.cipher.type);
$scope.savePromise = apiService.ciphers.put({ id: loginId }, login, function (loginResponse) { $scope.savePromise = apiService.ciphers.put({ id: cipherId }, cipher, function (cipherResponse) {
$analytics.eventTrack('Edited Login'); $analytics.eventTrack('Edited Cipher');
var decLogin = cipherService.decryptLogin(loginResponse); var decCipher = cipherService.decryptCipher(cipherResponse);
$uibModalInstance.close({ $uibModalInstance.close({
action: 'edit', action: 'edit',
data: decLogin data: decCipher
}); });
}).$promise; }).$promise;
} }
}; };
$scope.generatePassword = function () { $scope.generatePassword = function () {
if (!$scope.login.password || confirm('Are you sure you want to overwrite the current password?')) { if (!$scope.cipher.login.password || confirm('Are you sure you want to overwrite the current password?')) {
$analytics.eventTrack('Generated Password From Edit'); $analytics.eventTrack('Generated Password From Edit');
$scope.login.password = passwordService.generatePassword({ length: 12, special: true }); $scope.cipher.login.password = passwordService.generatePassword({ length: 12, special: true });
} }
}; };
$scope.addField = function () { $scope.addField = function () {
if (!$scope.login.fields) { if (!$scope.cipher.fields) {
$scope.login.fields = []; $scope.cipher.fields = [];
} }
$scope.login.fields.push({ $scope.cipher.fields.push({
type: '0', type: constants.fieldType.text,
name: null, name: null,
value: null value: null
}); });
}; };
$scope.removeField = function (field) { $scope.removeField = function (field) {
var index = $scope.login.fields.indexOf(field); var index = $scope.cipher.fields.indexOf(field);
if (index > -1) { if (index > -1) {
$scope.login.fields.splice(index, 1); $scope.cipher.fields.splice(index, 1);
} }
}; };
$scope.toggleFavorite = function () { $scope.toggleFavorite = function () {
$scope.login.favorite = !$scope.login.favorite; $scope.cipher.favorite = !$scope.cipher.favorite;
}; };
$scope.clipboardSuccess = function (e) { $scope.clipboardSuccess = function (e) {
@ -105,15 +106,15 @@
} }
$scope.delete = function () { $scope.delete = function () {
if (!confirm('Are you sure you want to delete this login (' + $scope.login.name + ')?')) { if (!confirm('Are you sure you want to delete this item (' + $scope.cipher.name + ')?')) {
return; return;
} }
apiService.ciphers.del({ id: $scope.login.id }, function () { apiService.ciphers.del({ id: $scope.cipher.id }, function () {
$analytics.eventTrack('Deleted Login From Edit'); $analytics.eventTrack('Deleted Cipher From Edit');
$uibModalInstance.close({ $uibModalInstance.close({
action: 'delete', action: 'delete',
data: $scope.login.id data: $scope.cipher.id
}); });
}); });
}; };

View File

@ -0,0 +1,254 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">
<i class="fa fa-globe"></i> Item Information <small>{{cipher.name}}</small>
</h4>
</div>
<form name="form" ng-submit="form.$valid && save(cipher)" api-form="savePromise" autocomplete="off">
<div class="modal-body">
<div class="callout callout-danger validation-errors" ng-show="form.$errors">
<h4>Errors have occurred</h4>
<ul>
<li ng-repeat="e in form.$errors">{{e}}</li>
</ul>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="name">Name</label> <span>*</span>
<input type="text" id="name" name="Name" ng-model="cipher.name" class="form-control"
ng-readonly="readOnly" required api-field />
</div>
</div>
<div class="col-sm-6" ng-if="!hideFolders">
<div class="form-group" show-errors>
<label for="folder">Folder</label>
<select id="folder" name="FolderId" ng-model="cipher.folderId" class="form-control" api-field>
<option ng-repeat="folder in folders | orderBy: folderSort" value="{{folder.id}}">
{{folder.name}}
</option>
</select>
</div>
</div>
</div>
<div ng-if="cipher.type == constants.cipherType.login">
<div class="form-group" show-errors>
<label for="uri">URI</label>
<div class="input-group">
<input type="text" id="uri" name="Login.Uri" ng-model="cipher.login.uri" class="form-control"
placeholder="http://..." ng-readonly="readOnly" api-field />
<span class="input-group-btn">
<button class="btn btn-default btn-flat" type="button" uib-tooltip="Copy URI"
tooltip-placement="left" ngclipboard ngclipboard-error="clipboardError(e)"
data-clipboard-target="#uri">
<i class="fa fa-clipboard"></i>
</button>
<a href="{{cipher.login.uri}}" target="_blank" class="btn btn-default btn-flat"
uib-tooltip="Go To Website" tooltip-placement="left">
<i class="fa fa-share"></i>
</a>
</span>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="username">Username</label>
<div class="input-group">
<input type="text" id="username" name="Login.Username" ng-model="cipher.login.username"
class="form-control" ng-readonly="readOnly" api-field />
<span class="input-group-btn" uib-tooltip="Copy Username" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-error="clipboardError(e)"
data-clipboard-target="#username">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group" show-errors style="margin-bottom: 5px;">
<div class="pull-right password-options">
<i class="fa fa-lg fa-refresh" uib-tooltip="Generate Password" tooltip-placement="left"
ng-click="generatePassword()" ng-show="!readOnly"></i>
<i class="fa fa-lg fa-eye" uib-tooltip="Toggle Password" tooltip-placement="left"
password-viewer="#password"></i>
</div>
<label for="password">Password</label>
<div class="input-group">
<input type="password" id="password" name="Login.Password" ng-model="cipher.login.password"
class="form-control" ng-readonly="readOnly" api-field />
<span class="input-group-btn" uib-tooltip="Copy Password" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)" ngclipboard-error="clipboardError(e, true)"
data-clipboard-text="{{cipher.login.password}}">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
</div>
<div password-meter="cipher.login.password" password-meter-username="cipher.login.username"
outer-class="xs" class="password-meter"></div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="totp">Authenticator Key (TOTP)</label>
<input type="text" id="totp" name="Login.Totp" ng-model="cipher.login.totp" class="form-control"
ng-readonly="readOnly" api-field />
</div>
</div>
<div class="col-sm-6 totp-col">
<div totp="cipher.login.totp" id="verification-code" ng-if="useTotp"></div>
<div ng-if="!useTotp">
<a href="#" stop-click ng-click="showUpgrade()"><img src="images/totp-countdown.png" alt="" /></a>
<span class="label label-info clickable" ng-click="showUpgrade()">
{{fromOrg ? 'UPGRADE' : 'PREMIUM'}}
</span>
</div>
</div>
</div>
</div>
<div ng-if="cipher.type == constants.cipherType.card">
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="cardholderName">Cardholder Name</label>
<div class="input-group">
<input type="text" id="cardholderName" name="CardholderName" ng-model="cipher.cardholderName"
class="form-control" ng-readonly="readOnly" api-field />
<span class="input-group-btn" uib-tooltip="Copy Cardholder Name" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-error="clipboardError(e)" data-clipboard-target="#cardholderName">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="cardBrand">Card Brand</label>
<select id="cardBrand" name="CardBrand" ng-model="cipher.brand" class="form-control" api-field>
<option value="visa">Visa</option>
<option value="masterCard">MasterCard</option>
<option value="discover">Discover</option>
<option value="amex">American Express</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="cardNumber">Card Number</label>
<div class="input-group">
<input type="text" id="cardNumber" name="Number" ng-model="cipher.number"
class="form-control" ng-readonly="readOnly" api-field />
<span class="input-group-btn" uib-tooltip="Copy Number" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-error="clipboardError(e)" data-clipboard-target="#cardNumber">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
</div>
</div>
<div class="col-sm-6">
</div>
</div>
</div>
<div ng-if="cipher.type == constants.cipherType.identity">
</div>
<div ng-if="cipher.type == constants.cipherType.secureNote">
</div>
<div class="form-group" show-errors>
<label for="notes">Notes</label>
<textarea id="notes" name="Notes" class="form-control" ng-model="cipher.notes"
ng-readonly="readOnly" api-field></textarea>
</div>
<div ng-if="!readOnly || (cipher.fields && cipher.fields.length)">
<hr />
<h4><i class="fa fa-list-ul"></i> Custom Fields</h4>
</div>
<div ng-repeat="field in cipher.fields">
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="field_name{{$index}}">Name</label>
<input type="text" id="field_name{{$index}}" class="form-control" ng-model="field.name"
ng-readonly="readOnly" />
</div>
</div>
<div class="col-sm-3">
<div class="form-group">
<label for="field_type{{$index}}">Type</label>
<select id="field_type{{$index}}" class="form-control" ng-model="field.type" ng-disabled="readOnly">
<option value="0">Text</option>
<option value="1">Hidden</option>
<option value="2">Boolean</option>
</select>
</div>
</div>
<div class="col-sm-5">
<div class="form-group">
<div class="pull-right password-options" ng-if="field.type === '1'">
<i class="fa fa-lg fa-eye" uib-tooltip="Toggle Visibility" tooltip-placement="left"
password-viewer="#field_value{{$index}}"></i>
</div>
<label for="field_value{{$index}}">Value</label>
<div class="input-group" ng-if="field.type !== '2'">
<input ng-attr-type="{{field.type === '0' ? 'text' : 'password'}}" id="field_value{{$index}}"
class="form-control" ng-model="field.value" ng-readonly="readOnly" />
<span class="input-group-btn" uib-tooltip="Copy Value" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)" ngclipboard-error="clipboardError(e, true)"
data-clipboard-text="{{field.value}}">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
<div ng-if="field.type === '2'">
<input type="checkbox" id="field_value{{$index}}" ng-model="field.value"
data-ng-true-value="'true'" ng-disabled="readOnly" />
</div>
</div>
</div>
<div class="col-sm-1">
<br class="hidden-xs" />
<a href="#" ng-click="removeField(field)" stop-click ng-if="!readOnly">
<i class="fa fa-window-close-o fa-lg"></i>
<span class="visible-xs-inline">Remove Custom Field</span>
</a>
</div>
</div>
<hr class="visible-xs-block" />
</div>
<a href="#" ng-click="addField()" stop-click ng-if="!readOnly">
<i class="fa fa-plus-circle"></i> New Custom Field
</a>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Save
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
<button type="button" class="btn btn-link pull-right" ng-click="delete()" uib-tooltip="Delete"
tooltip-placement="left" ng-disabled="readOnly">
<i class="fa fa-trash fa-lg"></i>
<span class="sr-only">Delete</span>
</button>
<button type="button" ng-if="!hideFavorite" class="btn btn-link pull-right" ng-click="toggleFavorite()"
uib-tooltip="Toggle Favorite" tooltip-placement="left">
<i class="fa fa-lg" ng-class="cipher.favorite ? 'fa-star' : 'fa-star-o'"></i>
<span class="sr-only">Toggle Favorite</span>
</button>
</div>
</form>

View File

@ -1,192 +0,0 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="editLoginModelLabel">
<i class="fa fa-globe"></i> Login Information <small>{{login.name}}</small>
</h4>
</div>
<form name="editLoginForm" ng-submit="editLoginForm.$valid && save(login)" api-form="savePromise" autocomplete="off">
<div class="modal-body">
<div class="callout callout-danger validation-errors" ng-show="editLoginForm.$errors">
<h4>Errors have occurred</h4>
<ul>
<li ng-repeat="e in editLoginForm.$errors">{{e}}</li>
</ul>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="name">Name</label> <span>*</span>
<input type="text" id="name" name="Name" ng-model="login.name" class="form-control"
ng-readonly="readOnly" required api-field />
</div>
</div>
<div class="col-sm-6" ng-if="!hideFolders">
<div class="form-group" show-errors>
<label for="folder">Folder</label>
<select id="folder" name="FolderId" ng-model="login.folderId" class="form-control" api-field>
<option ng-repeat="folder in folders | orderBy: folderSort" value="{{folder.id}}">
{{folder.name}}
</option>
</select>
</div>
</div>
</div>
<div class="form-group" show-errors>
<label for="uri">URI</label>
<div class="input-group">
<input type="text" id="uri" name="Uri" ng-model="login.uri" class="form-control" placeholder="http://..."
ng-readonly="readOnly" api-field />
<span class="input-group-btn">
<button class="btn btn-default btn-flat" type="button" uib-tooltip="Copy URI"
tooltip-placement="left" ngclipboard ngclipboard-error="clipboardError(e)"
data-clipboard-target="#uri">
<i class="fa fa-clipboard"></i>
</button>
<a href="{{login.uri}}" target="_blank" class="btn btn-default btn-flat" uib-tooltip="Go To Login"
tooltip-placement="left">
<i class="fa fa-share"></i>
</a>
</span>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="username">Username</label>
<div class="input-group">
<input type="text" id="username" name="Username" ng-model="login.username" class="form-control"
ng-readonly="readOnly" api-field />
<span class="input-group-btn" uib-tooltip="Copy Username" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-error="clipboardError(e)"
data-clipboard-target="#username">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group" show-errors style="margin-bottom: 5px;">
<div class="pull-right password-options">
<i class="fa fa-lg fa-refresh" uib-tooltip="Generate Password" tooltip-placement="left"
ng-click="generatePassword()" ng-show="!readOnly"></i>
<i class="fa fa-lg fa-eye" uib-tooltip="Toggle Password" tooltip-placement="left"
password-viewer="#password"></i>
</div>
<label for="password">Password</label>
<div class="input-group">
<input type="password" id="password" name="Password" ng-model="login.password" class="form-control"
ng-readonly="readOnly" api-field />
<span class="input-group-btn" uib-tooltip="Copy Password" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)" ngclipboard-error="clipboardError(e, true)"
data-clipboard-text="{{login.password}}">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
</div>
<div password-meter="login.password" password-meter-username="login.username"
outer-class="xs" class="password-meter"></div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
<label for="totp">Authenticator Key (TOTP)</label>
<input type="text" id="totp" name="Totp" ng-model="login.totp" class="form-control"
ng-readonly="readOnly" api-field />
</div>
</div>
<div class="col-sm-6 totp-col">
<div totp="login.totp" id="verification-code" ng-if="useTotp"></div>
<div ng-if="!useTotp">
<a href="#" stop-click ng-click="showUpgrade()"><img src="images/totp-countdown.png" alt="" /></a>
<span class="label label-info clickable" ng-click="showUpgrade()">{{fromOrg ? 'UPGRADE' : 'PREMIUM'}}</span>
</div>
</div>
</div>
<div class="form-group" show-errors>
<label for="notes">Notes</label>
<textarea id="notes" name="Notes" class="form-control" ng-model="login.notes"
ng-readonly="readOnly" api-field></textarea>
</div>
<div ng-if="!readOnly || (login.fields && login.fields.length)">
<hr />
<h4><i class="fa fa-list-ul"></i> Custom Fields</h4>
</div>
<div ng-repeat="field in login.fields">
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="field_name{{$index}}">Name</label>
<input type="text" id="field_name{{$index}}" class="form-control" ng-model="field.name"
ng-readonly="readOnly" />
</div>
</div>
<div class="col-sm-3">
<div class="form-group">
<label for="field_type{{$index}}">Type</label>
<select id="field_type{{$index}}" class="form-control" ng-model="field.type" ng-disabled="readOnly">
<option value="0">Text</option>
<option value="1">Hidden</option>
<option value="2">Boolean</option>
</select>
</div>
</div>
<div class="col-sm-5">
<div class="form-group">
<div class="pull-right password-options" ng-if="field.type === '1'">
<i class="fa fa-lg fa-eye" uib-tooltip="Toggle Visibility" tooltip-placement="left"
password-viewer="#field_value{{$index}}"></i>
</div>
<label for="field_value{{$index}}">Value</label>
<div class="input-group" ng-if="field.type !== '2'">
<input ng-attr-type="{{field.type === '0' ? 'text' : 'password'}}" id="field_value{{$index}}"
class="form-control" ng-model="field.value" ng-readonly="readOnly" />
<span class="input-group-btn" uib-tooltip="Copy Value" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)" ngclipboard-error="clipboardError(e, true)"
data-clipboard-text="{{field.value}}">
<i class="fa fa-clipboard"></i>
</button>
</span>
</div>
<div ng-if="field.type === '2'">
<input type="checkbox" id="field_value{{$index}}" ng-model="field.value"
data-ng-true-value="'true'" ng-disabled="readOnly" />
</div>
</div>
</div>
<div class="col-sm-1">
<br class="hidden-xs" />
<a href="#" ng-click="removeField(field)" stop-click ng-if="!readOnly">
<i class="fa fa-window-close-o fa-lg"></i>
<span class="visible-xs-inline">Remove Custom Field</span>
</a>
</div>
</div>
<hr class="visible-xs-block" />
</div>
<a href="#" ng-click="addField()" stop-click ng-if="!readOnly">
<i class="fa fa-plus-circle"></i> New Custom Field
</a>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="editLoginForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="editLoginForm.$loading"></i>Save
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
<button type="button" class="btn btn-link pull-right" ng-click="delete()" uib-tooltip="Delete"
tooltip-placement="left" ng-disabled="readOnly">
<i class="fa fa-trash fa-lg"></i>
<span class="sr-only">Delete</span>
</button>
<button type="button" ng-if="!hideFavorite" class="btn btn-link pull-right" ng-click="toggleFavorite()"
uib-tooltip="Toggle Favorite" tooltip-placement="left">
<i class="fa fa-lg" ng-class="login.favorite ? 'fa-star' : 'fa-star-o'"></i>
<span class="sr-only">Toggle Favorite</span>
</button>
</div>
</form>

View File

@ -210,7 +210,7 @@
<script src="app/vault/vaultModule.js"></script> <script src="app/vault/vaultModule.js"></script>
<script src="app/vault/vaultController.js"></script> <script src="app/vault/vaultController.js"></script>
<script src="app/vault/vaultEditLoginController.js"></script> <script src="app/vault/vaultEditCipherController.js"></script>
<script src="app/vault/vaultAddLoginController.js"></script> <script src="app/vault/vaultAddLoginController.js"></script>
<script src="app/vault/vaultEditFolderController.js"></script> <script src="app/vault/vaultEditFolderController.js"></script>
<script src="app/vault/vaultAddFolderController.js"></script> <script src="app/vault/vaultAddFolderController.js"></script>