mirror of https://github.com/keeweb/keeweb
adding multiple websites to entries
parent
00a2a95adc
commit
bb78e8e8a7
|
@ -286,7 +286,8 @@
|
|||
"detCopyHint": "You can copy field value by clicking its title",
|
||||
"detMore": "more",
|
||||
"detClickToAddField": "click to add a new field",
|
||||
"detMenuAddNewField": "Add new field",
|
||||
"detMenuAddNewField": "Add a new field",
|
||||
"detMenuAddNewWebsite": "Add another website",
|
||||
"detMenuShowEmpty": "Show empty fields",
|
||||
"detMenuHideEmpty": "Hide empty fields",
|
||||
"detMenuAddField": "Add {}",
|
||||
|
|
|
@ -16,6 +16,7 @@ const UrlRegex = /^https?:\/\//i;
|
|||
const FieldRefRegex = /^\{REF:([TNPAU])@I:(\w{32})}$/;
|
||||
const FieldRefFields = ['title', 'password', 'user', 'url', 'notes'];
|
||||
const FieldRefIds = { T: 'Title', U: 'UserName', P: 'Password', A: 'URL', N: 'Notes' };
|
||||
const ExtraUrlFieldName = 'KP2A_URL';
|
||||
|
||||
class EntryModel extends Model {
|
||||
constructor(props) {
|
||||
|
@ -639,6 +640,18 @@ class EntryModel extends Model {
|
|||
this._entryModified();
|
||||
}
|
||||
|
||||
getNextUrlFieldName() {
|
||||
const takenFields = new Set(
|
||||
Object.keys(this.entry.fields).filter((f) => f.startsWith(ExtraUrlFieldName))
|
||||
);
|
||||
for (let i = 0; ; i++) {
|
||||
const fieldName = i ? `${ExtraUrlFieldName}_${i}` : ExtraUrlFieldName;
|
||||
if (!takenFields.has(fieldName)) {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static fromEntry(entry, group, file) {
|
||||
const model = new EntryModel();
|
||||
model.setEntry(entry, group, file);
|
||||
|
@ -667,4 +680,4 @@ class EntryModel extends Model {
|
|||
|
||||
EntryModel.defineModelProperties({}, { extensions: true });
|
||||
|
||||
export { EntryModel };
|
||||
export { EntryModel, ExtraUrlFieldName };
|
||||
|
|
|
@ -13,6 +13,7 @@ import { FieldViewDate } from 'views/fields/field-view-date';
|
|||
import { FieldViewHistory } from 'views/fields/field-view-history';
|
||||
import { FieldViewCustom } from 'views/fields/field-view-custom';
|
||||
import { FieldViewReadOnlyWithOptions } from 'views/fields/field-view-read-only-with-options';
|
||||
import { ExtraUrlFieldName } from 'models/entry-model';
|
||||
|
||||
function createDetailsFields(detailsView) {
|
||||
const model = detailsView.model;
|
||||
|
@ -238,11 +239,13 @@ function createDetailsFields(detailsView) {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
const isUrl = field.startsWith(ExtraUrlFieldName);
|
||||
fieldViews.push(
|
||||
new FieldViewCustom({
|
||||
name: '$' + field,
|
||||
title: field,
|
||||
multiline: true,
|
||||
title: isUrl ? StringFormat.capFirst(Locale.website) : field,
|
||||
multiline: !isUrl,
|
||||
titleEditable: !isUrl,
|
||||
value() {
|
||||
return model.fields[field];
|
||||
},
|
||||
|
|
|
@ -30,6 +30,8 @@ import template from 'templates/details/details.hbs';
|
|||
import emptyTemplate from 'templates/details/details-empty.hbs';
|
||||
import groupTemplate from 'templates/details/details-group.hbs';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
import { ExtraUrlFieldName } from 'models/entry-model';
|
||||
import { StringFormat } from '../../util/formatting/string-format';
|
||||
|
||||
class DetailsView extends View {
|
||||
parent = '.app__details';
|
||||
|
@ -196,10 +198,10 @@ class DetailsView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
addNewField() {
|
||||
addNewField(title) {
|
||||
this.moreView.remove();
|
||||
this.moreView = null;
|
||||
let newFieldTitle = Locale.detNetField;
|
||||
let newFieldTitle = title || Locale.detNetField;
|
||||
if (this.model.fields[newFieldTitle]) {
|
||||
for (let i = 1; ; i++) {
|
||||
const newFieldTitleVariant = newFieldTitle + i;
|
||||
|
@ -209,12 +211,15 @@ class DetailsView extends View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isUrl = newFieldTitle.startsWith(ExtraUrlFieldName);
|
||||
const fieldView = new FieldViewCustom(
|
||||
{
|
||||
name: '$' + newFieldTitle,
|
||||
title: newFieldTitle,
|
||||
title: isUrl ? StringFormat.capFirst(Locale.website) : newFieldTitle,
|
||||
newField: newFieldTitle,
|
||||
multiline: true,
|
||||
multiline: !isUrl,
|
||||
titleEditable: !isUrl,
|
||||
value() {
|
||||
return '';
|
||||
}
|
||||
|
@ -223,6 +228,7 @@ class DetailsView extends View {
|
|||
parent: this.$el.find('.details__body-fields')[0]
|
||||
}
|
||||
);
|
||||
|
||||
fieldView.on('change', this.fieldChanged.bind(this));
|
||||
fieldView.render();
|
||||
fieldView.edit();
|
||||
|
@ -255,6 +261,13 @@ class DetailsView extends View {
|
|||
icon: 'plus',
|
||||
text: Locale.detMenuAddNewField
|
||||
});
|
||||
if (this.model.url) {
|
||||
moreOptions.push({
|
||||
value: 'add-website',
|
||||
icon: 'plus',
|
||||
text: Locale.detMenuAddNewWebsite
|
||||
});
|
||||
}
|
||||
moreOptions.push({
|
||||
value: 'toggle-empty',
|
||||
icon: 'eye',
|
||||
|
@ -266,6 +279,13 @@ class DetailsView extends View {
|
|||
icon: 'plus',
|
||||
text: Locale.detMenuAddNewField
|
||||
});
|
||||
if (this.model.url) {
|
||||
moreOptions.push({
|
||||
value: 'add-website',
|
||||
icon: 'plus',
|
||||
text: Locale.detMenuAddNewWebsite
|
||||
});
|
||||
}
|
||||
moreOptions.push({
|
||||
value: 'toggle-empty',
|
||||
icon: 'eye-slash',
|
||||
|
@ -303,6 +323,9 @@ class DetailsView extends View {
|
|||
case 'add-new':
|
||||
this.addNewField();
|
||||
break;
|
||||
case 'add-website':
|
||||
this.addNewField(this.model.getNextUrlFieldName());
|
||||
break;
|
||||
case 'toggle-empty': {
|
||||
const hideEmptyFields = AppSettingsModel.hideEmptyFields;
|
||||
AppSettingsModel.hideEmptyFields = !hideEmptyFields;
|
||||
|
|
|
@ -96,9 +96,10 @@ class FieldViewCustom extends FieldViewText {
|
|||
|
||||
fieldLabelClick(e) {
|
||||
e.stopImmediatePropagation();
|
||||
if (this.model.newField) {
|
||||
|
||||
if (this.model.titleEditable && this.model.newField) {
|
||||
this.startEditTitle(true);
|
||||
} else if (this.editing) {
|
||||
} else if (this.model.titleEditable && this.editing) {
|
||||
this.startEditTitle();
|
||||
} else {
|
||||
super.fieldLabelClick.call(this, e);
|
||||
|
|
Loading…
Reference in New Issue