[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": {
|
"cfTypeBoolean": {
|
||||||
"message": "Boolean"
|
"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": {
|
"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?"
|
"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": {
|
"lastName": {
|
||||||
"message": "Last Name"
|
"message": "Last Name"
|
||||||
},
|
},
|
||||||
|
"fullName": {
|
||||||
|
"message": "Full Name"
|
||||||
|
},
|
||||||
"identityName": {
|
"identityName": {
|
||||||
"message": "Identity Name"
|
"message": "Identity Name"
|
||||||
},
|
},
|
||||||
|
|
|
@ -271,7 +271,7 @@ export default class MainBackground {
|
||||||
const message = Object.assign({}, { command: subscriber }, arg);
|
const message = Object.assign({}, { command: subscriber }, arg);
|
||||||
that.runtimeBackground.processMessage(message, that, null);
|
that.runtimeBackground.processMessage(message, that, null);
|
||||||
}
|
}
|
||||||
}(), this.vaultTimeoutService, this.logService);
|
}(), this.vaultTimeoutService, this.logService, this.cryptoFunctionService);
|
||||||
}
|
}
|
||||||
|
|
||||||
async bootstrap() {
|
async bootstrap() {
|
||||||
|
|
|
@ -531,6 +531,10 @@
|
||||||
color: themed('mutedColor');
|
color: themed('mutedColor');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-small {
|
||||||
|
min-width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
max-height: 20px;
|
max-height: 20px;
|
||||||
|
|
|
@ -16,15 +16,20 @@
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name" class="row-label"
|
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name" class="row-label"
|
||||||
placeholder="{{'name' | i18n}}" appInputVerbatim>
|
placeholder="{{'name' | i18n}}" appInputVerbatim>
|
||||||
<!--Custom Field: Text-->
|
<!-- Text -->
|
||||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}" appInputVerbatim>
|
*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}}"
|
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}" name="Field.Value{{i}}"
|
||||||
[(ngModel)]="f.value" class="monospaced" appInputVerbatim *ngIf="f.type === fieldType.Hidden"
|
[(ngModel)]="f.value" class="monospaced" appInputVerbatim *ngIf="f.type === fieldType.Hidden"
|
||||||
placeholder="{{'value' | i18n}}" [disabled]="!cipher.viewPassword && !f.newField">
|
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>
|
</div>
|
||||||
<!--Custom Field: Boolean-->
|
<!-- Boolean -->
|
||||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox" [(ngModel)]="f.value"
|
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox" [(ngModel)]="f.value"
|
||||||
*ngIf="f.type === fieldType.Boolean" appTrueFalseValue trueValue="true" falseValue="false">
|
*ngIf="f.type === fieldType.Boolean" appTrueFalseValue trueValue="true" falseValue="false">
|
||||||
<div class="action-buttons" *ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)">
|
<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>
|
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||||
|
<option *ngIf="cipher.linkedFieldOptions != null" [ngValue]="addFieldLinkedTypeOption.value">
|
||||||
|
{{addFieldLinkedTypeOption.name}}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -324,7 +324,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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" *ngIf="allowOwnershipOptions()">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'ownership' | i18n}}
|
{{'ownership' | i18n}}
|
||||||
|
|
|
@ -18,6 +18,13 @@
|
||||||
<i class="fa fa-square-o" *ngIf="field.value !== 'true'" aria-hidden="true"></i>
|
<i class="fa fa-square-o" *ngIf="field.value !== 'true'" aria-hidden="true"></i>
|
||||||
<span class="sr-only">{{field.value}}</span>
|
<span class="sr-only">{{field.value}}</span>
|
||||||
</div>
|
</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>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button type="button" class="row-btn" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
<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>
|
[ngClass]="{'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue}"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="row-btn" appStopClick appA11yTitle="{{'copyValue' | i18n}}"
|
<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')">
|
(click)="copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')">
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { EventType } from 'jslib-common/enums/eventType';
|
||||||
import { FieldType } from 'jslib-common/enums/fieldType';
|
import { FieldType } from 'jslib-common/enums/fieldType';
|
||||||
|
|
||||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||||
|
import { FieldView } from 'jslib-common/models/view/fieldView';
|
||||||
|
|
||||||
import AutofillField from '../models/autofillField';
|
import AutofillField from '../models/autofillField';
|
||||||
import AutofillPageDetails from '../models/autofillPageDetails';
|
import AutofillPageDetails from '../models/autofillPageDetails';
|
||||||
|
@ -318,10 +319,17 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||||
|
|
||||||
const matchingIndex = this.findMatchingFieldIndex(field, fieldNames);
|
const matchingIndex = this.findMatchingFieldIndex(field, fieldNames);
|
||||||
if (matchingIndex > -1) {
|
if (matchingIndex > -1) {
|
||||||
let val = fields[matchingIndex].value;
|
const matchingField: FieldView = fields[matchingIndex];
|
||||||
if (val == null && fields[matchingIndex].type === FieldType.Boolean) {
|
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';
|
val = 'false';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
filledFields[field.opid] = field;
|
filledFields[field.opid] = field;
|
||||||
this.fillByOpid(fillScript, field, val);
|
this.fillByOpid(fillScript, field, val);
|
||||||
|
|
Loading…
Reference in New Issue