[Linked fields] Add Linked Field as a custom field type (#1963)
* Proof of concept for Linked custom field type * Linked Fields for all cipher types, use dropdown * Fix linked icon alignment * Tweak linked icon alignment and style * Move add-edit custom fields to own component * Disable copy for linked field * Use Field.LinkedId to store linked field info
This commit is contained in:
parent
f20a1e7424
commit
2113c709a4
2
jslib
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit 24fe836032354d4ec39435776e54dd0995e1b389
|
||||
Subproject commit 2db9e1ce0d7a702f07f20ecb916dd8191ff617e1
|
|
@ -983,6 +983,14 @@
|
|||
"cfTypeBoolean": {
|
||||
"message": "Boolean"
|
||||
},
|
||||
"cfTypeLinked": {
|
||||
"message": "Linked",
|
||||
"description": "This describes a field that is 'linked' (tied) to another field."
|
||||
},
|
||||
"linkedValue": {
|
||||
"message": "Linked value",
|
||||
"description": "This describes a value that is 'linked' (tied) to another value."
|
||||
},
|
||||
"popup2faCloseMessage": {
|
||||
"message": "Clicking outside the popup window to check your email for your verification code will cause this popup to close. Do you want to open this popup in a new window so that it does not close?"
|
||||
},
|
||||
|
@ -1085,6 +1093,9 @@
|
|||
"lastName": {
|
||||
"message": "Last Name"
|
||||
},
|
||||
"fullName": {
|
||||
"message": "Full Name"
|
||||
},
|
||||
"identityName": {
|
||||
"message": "Identity Name"
|
||||
},
|
||||
|
|
|
@ -271,7 +271,7 @@ export default class MainBackground {
|
|||
const message = Object.assign({}, { command: subscriber }, arg);
|
||||
that.runtimeBackground.processMessage(message, that, null);
|
||||
}
|
||||
}(), this.vaultTimeoutService, this.logService);
|
||||
}(), this.vaultTimeoutService, this.logService, this.cryptoFunctionService);
|
||||
}
|
||||
|
||||
async bootstrap() {
|
||||
|
|
|
@ -531,6 +531,10 @@
|
|||
color: themed('mutedColor');
|
||||
}
|
||||
|
||||
&.icon-small {
|
||||
min-width: 25px;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: $border-radius;
|
||||
max-height: 20px;
|
||||
|
|
|
@ -16,15 +16,20 @@
|
|||
<div class="row-main">
|
||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name" class="row-label"
|
||||
placeholder="{{'name' | i18n}}" appInputVerbatim>
|
||||
<!--Custom Field: Text-->
|
||||
<!-- Text -->
|
||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}" appInputVerbatim>
|
||||
<!--Custom Field: Hidden-->
|
||||
<!-- Hidden -->
|
||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}" name="Field.Value{{i}}"
|
||||
[(ngModel)]="f.value" class="monospaced" appInputVerbatim *ngIf="f.type === fieldType.Hidden"
|
||||
placeholder="{{'value' | i18n}}" [disabled]="!cipher.viewPassword && !f.newField">
|
||||
<!-- Linked -->
|
||||
<select id="fieldValue{{i}}" name="Field.Value{{i}}" [(ngModel)]="f.linkedId"
|
||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null">
|
||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!--Custom Field: Boolean-->
|
||||
<!-- Boolean -->
|
||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Boolean" appTrueFalseValue trueValue="true" falseValue="false">
|
||||
<div class="action-buttons" *ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)">
|
||||
|
@ -47,6 +52,9 @@
|
|||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
<option *ngIf="cipher.linkedFieldOptions != null" [ngValue]="addFieldLinkedTypeOption.value">
|
||||
{{addFieldLinkedTypeOption.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -324,7 +324,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-add-edit-custom-fields [cipher]="cipher" [editMode]="editMode"></app-vault-add-edit-custom-fields>
|
||||
<app-vault-add-edit-custom-fields [cipher]="cipher" [thisCipherType]="cipher.type" [editMode]="editMode">
|
||||
</app-vault-add-edit-custom-fields>
|
||||
<div class="box" *ngIf="allowOwnershipOptions()">
|
||||
<div class="box-header">
|
||||
{{'ownership' | i18n}}
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
<i class="fa fa-square-o" *ngIf="field.value !== 'true'" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{field.value}}</span>
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Linked" class="box-content-row-flex">
|
||||
<div class="icon icon-small">
|
||||
<i class="fa fa-link" aria-hidden="true" appA11yTitle="{{'linkedValue' | i18n}}"></i>
|
||||
<span class="sr-only">{{'linkedValue' | i18n}}</span>
|
||||
</div>
|
||||
<span>{{cipher.linkedFieldI18nKey(field.linkedId) | i18n}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
|
@ -27,7 +34,8 @@
|
|||
[ngClass]="{'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue}"></i>
|
||||
</button>
|
||||
<button type="button" class="row-btn" appStopClick appA11yTitle="{{'copyValue' | i18n}}"
|
||||
*ngIf="field.value && field.type !== fieldType.Boolean && !(field.type === fieldType.Hidden && !cipher.viewPassword)"
|
||||
*ngIf="field.value && field.type !== fieldType.Boolean && field.type !== fieldType.Linked &&
|
||||
!(field.type === fieldType.Hidden && !cipher.viewPassword)"
|
||||
(click)="copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
|
|
@ -12,6 +12,7 @@ import { EventType } from 'jslib-common/enums/eventType';
|
|||
import { FieldType } from 'jslib-common/enums/fieldType';
|
||||
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { FieldView } from 'jslib-common/models/view/fieldView';
|
||||
|
||||
import AutofillField from '../models/autofillField';
|
||||
import AutofillPageDetails from '../models/autofillPageDetails';
|
||||
|
@ -318,9 +319,16 @@ export default class AutofillService implements AutofillServiceInterface {
|
|||
|
||||
const matchingIndex = this.findMatchingFieldIndex(field, fieldNames);
|
||||
if (matchingIndex > -1) {
|
||||
let val = fields[matchingIndex].value;
|
||||
if (val == null && fields[matchingIndex].type === FieldType.Boolean) {
|
||||
val = 'false';
|
||||
const matchingField: FieldView = fields[matchingIndex];
|
||||
let val;
|
||||
if (matchingField.type === FieldType.Linked) {
|
||||
// Assumption: Linked Field is not being used to autofill a boolean value
|
||||
val = options.cipher.linkedFieldValue(matchingField.linkedId);
|
||||
} else {
|
||||
val = matchingField.value;
|
||||
if (val == null && matchingField.type === FieldType.Boolean) {
|
||||
val = 'false';
|
||||
}
|
||||
}
|
||||
|
||||
filledFields[field.opid] = field;
|
||||
|
|
Loading…
Reference in New Issue