menu models

pull/1267/head
antelle 2019-09-18 23:37:57 +02:00
parent 2f93e53716
commit 4d7cef70d2
32 changed files with 457 additions and 471 deletions

View File

@ -293,7 +293,7 @@ AutoTypeRunner.prototype.getEntryFieldKeys = function(field, op) {
};
AutoTypeRunner.prototype.getEntryGroupName = function() {
return this.entry && this.entry.group.get('title');
return this.entry && this.entry.group.title;
};
AutoTypeRunner.prototype.dt = function(part) {

View File

@ -26,7 +26,8 @@ const AutoType = {
}
this.appModel = appModel;
Events.on('auto-type', e => this.handleEvent(e));
Events.on('main-window-blur main-window-will-close', e => this.resetPendingEvent(e));
Events.on('main-window-blur', e => this.resetPendingEvent(e));
Events.on('main-window-will-close', e => this.resetPendingEvent(e));
},
handleEvent(e) {

View File

@ -1,13 +1,11 @@
import Backbone from 'backbone';
import { Collection } from 'framework/collection';
import { EntryModel } from 'models/entry-model';
import { Comparators } from 'util/data/comparators';
const EntryCollection = Backbone.Collection.extend({
model: EntryModel,
class EntryCollection extends Collection {
static model = EntryModel;
comparator: null,
comparators: {
comparators = {
'none': null,
'title': Comparators.stringComparator('title', true),
'-title': Comparators.stringComparator('title', false),
@ -23,22 +21,23 @@ const EntryCollection = Backbone.Collection.extend({
return this.attachmentSortVal(x).localeCompare(this.attachmentSortVal(y));
},
'-rank': Comparators.rankComparator()
},
};
defaultComparator: 'title',
defaultComparator = 'title';
filter: null,
filter = null;
initialize(models, options) {
constructor(models, options) {
super(models);
const comparatorName = (options && options.comparator) || this.defaultComparator;
this.comparator = this.comparators[comparatorName];
},
}
sortEntries(comparator, filter) {
this.filter = filter;
this.comparator = this.comparators[comparator] || this.comparators[this.defaultComparator];
this.sort();
},
}
attachmentSortVal(entry) {
const att = entry.attachments;
@ -48,6 +47,6 @@ const EntryCollection = Backbone.Collection.extend({
}
return str;
}
});
}
export { EntryCollection };

View File

@ -1,8 +1,8 @@
import Backbone from 'backbone';
import { Collection } from 'framework/collection';
import { GroupModel } from 'models/group-model';
const GroupCollection = Backbone.Collection.extend({
model: GroupModel
});
class GroupCollection extends Collection {
static model = GroupModel;
}
export { GroupCollection };

View File

@ -1,8 +1,8 @@
import Backbone from 'backbone';
import { Collection } from 'framework/collection';
import { MenuItemModel } from 'models/menu/menu-item-model';
const MenuItemCollection = Backbone.Collection.extend({
model: MenuItemModel
});
class MenuItemCollection extends Collection {
static model = MenuItemModel;
}
export { MenuItemCollection };

View File

@ -1,8 +1,8 @@
import Backbone from 'backbone';
import { Collection } from 'framework/collection';
import { MenuOptionModel } from 'models/menu/menu-option-model';
const MenuOptionCollection = Backbone.Collection.extend({
model: MenuOptionModel
});
class MenuOptionCollection extends Collection {
static model = MenuOptionModel;
}
export { MenuOptionCollection };

View File

@ -1,8 +1,8 @@
import Backbone from 'backbone';
import { Collection } from 'framework/collection';
import { MenuSectionModel } from 'models/menu/menu-section-model';
const MenuSectionCollection = Backbone.Collection.extend({
model: MenuSectionModel
});
class MenuSectionCollection extends Collection {
static model = MenuSectionModel;
}
export { MenuSectionCollection };

View File

@ -40,7 +40,8 @@ const ProxyDef = {
}
const numProp = parseInt(property);
if (isNaN(numProp)) {
return false;
target[property] = value;
return true;
}
const modelClass = target.constructor.model;
if (!modelClass) {
@ -61,8 +62,8 @@ const ProxyDef = {
};
class Collection extends Array {
constructor(...args) {
super(...args);
constructor(items) {
super();
const emitter = new EventEmitter();
emitter.setMaxListeners(100);
@ -73,7 +74,13 @@ class Collection extends Array {
Object.defineProperties(this, properties);
return new Proxy(this, ProxyDef);
const object = new Proxy(this, ProxyDef);
if (items) {
object.push(...items);
}
return object;
}
push(...items) {

View File

@ -40,7 +40,10 @@ const ProxyDef = {
}
return true;
} else {
new Logger(receiver.constructor.name).warn(`Unknown property: ${property}`);
new Logger(receiver.constructor.name).warn(
`Unknown property: ${property}`,
new Error().stack
);
}
return false;
}
@ -106,7 +109,7 @@ class Model {
}
static defineModelProperties(properties, options) {
this.prototype[SymbolDefaults] = properties;
this.prototype[SymbolDefaults] = { ...this.prototype[SymbolDefaults], ...properties };
if (options && options.extensions) {
this.prototype[SymbolExtensions] = true;
}

View File

@ -158,9 +158,9 @@ class AppModel {
return false;
}
this.files.push(file);
file.groups.forEach(function(group) {
for (const group of file.groups) {
this.menu.groupsSection.addItem(group);
}, this);
}
this._addTags(file);
this._tagsChanged();
this.menu.filesSection.addItem({
@ -175,7 +175,7 @@ class AppModel {
}
reloadFile(file) {
this.menu.groupsSection.replaceByFile(file, file.groups.first());
this.menu.groupsSection.replaceByFile(file, file.groups[0]);
this.updateTags();
}
@ -197,7 +197,7 @@ class AppModel {
_tagsChanged() {
if (this.tags.length) {
this.menu.tagsSection.set('scrollable', true);
this.menu.tagsSection.scrollable = true;
this.menu.tagsSection.setItems(
this.tags.map(tag => {
return {
@ -210,7 +210,7 @@ class AppModel {
})
);
} else {
this.menu.tagsSection.set('scrollable', false);
this.menu.tagsSection.scrollable = false;
this.menu.tagsSection.removeAllItems();
}
}
@ -238,7 +238,7 @@ class AppModel {
}
this.files.length = 0;
this.menu.groupsSection.removeAllItems();
this.menu.tagsSection.set('scrollable', false);
this.menu.tagsSection.scrollable = false;
this.menu.tagsSection.removeAllItems();
this.menu.filesSection.removeAllItems();
this.tags.splice(0, this.tags.length);
@ -253,7 +253,7 @@ class AppModel {
this.updateTags();
this.menu.groupsSection.removeByFile(file);
this.menu.filesSection.removeByFile(file);
this.menu.select({ item: this.menu.allItemsSection.get('items').first() });
this.menu.select({ item: this.menu.allItemsSection.items[0] });
}
emptyTrash() {
@ -269,7 +269,7 @@ class AppModel {
}
const entries = this.getEntries();
if (!this.activeEntryId || !entries.get(this.activeEntryId)) {
const firstEntry = entries.first();
const firstEntry = entries[0];
this.activeEntryId = firstEntry ? firstEntry.id : null;
}
Events.emit('filter', { filter: this.filter, sort: this.sort, entries });
@ -341,8 +341,8 @@ class AppModel {
});
}
if (!group) {
file = this.files.first();
group = file.groups.first();
file = this.files[0];
group = file.groups[0];
}
return { group, file };
}
@ -384,7 +384,7 @@ class AppModel {
if (args && args.template) {
if (sel.file !== args.template.file) {
sel.file = args.template.file;
sel.group = args.template.file.groups.first();
sel.group = args.template.file.groups[0];
}
const templateEntry = args.template.entry;
const newEntry = EntryModel.newEntry(sel.group, sel.file);

View File

@ -1,5 +1,5 @@
import Backbone from 'backbone';
import kdbxweb from 'kdbxweb';
import { Model } from 'framework/model';
import { KdbxToHtml } from 'comp/format/kdbx-to-html';
import { IconMap } from 'const/icon-map';
import { AttachmentModel } from 'models/attachment-model';
@ -9,43 +9,40 @@ import { Ranking } from 'util/data/ranking';
import { IconUrlFormat } from 'util/formatting/icon-url-format';
import { omit } from 'util/fn';
const EntryModel = Backbone.Model.extend({
defaults: {},
urlRegex: /^https?:\/\//i,
fieldRefRegex: /^\{REF:([TNPAU])@I:(\w{32})}$/,
builtInFields: [
'Title',
'Password',
'UserName',
'URL',
'Notes',
'TOTP Seed',
'TOTP Settings',
'_etm_template_uuid'
],
fieldRefFields: ['title', 'password', 'user', 'url', 'notes'],
fieldRefIds: { T: 'Title', U: 'UserName', P: 'Password', A: 'URL', N: 'Notes' },
const UrlRegex = /^https?:\/\//i;
const FieldRefRegex = /^\{REF:([TNPAU])@I:(\w{32})}$/;
const BuiltInFields = [
'Title',
'Password',
'UserName',
'URL',
'Notes',
'TOTP Seed',
'TOTP Settings',
'_etm_template_uuid'
];
const FieldRefFields = ['title', 'password', 'user', 'url', 'notes'];
const FieldRefIds = { T: 'Title', U: 'UserName', P: 'Password', A: 'URL', N: 'Notes' };
class EntryModel extends Model {
setEntry(entry, group, file) {
this.entry = entry;
this.group = group;
this.file = file;
if (this.get('uuid') === entry.uuid.id) {
if (this.uuid === entry.uuid.id) {
this._checkUpdatedEntry();
}
// we cannot calculate field references now because database index has not yet been built
this.hasFieldRefs = false;
this._fillByEntry();
this.hasFieldRefs = true;
},
}
_fillByEntry() {
const entry = this.entry;
this.set({ id: this.file.subId(entry.uuid.id), uuid: entry.uuid.id }, { silent: true });
this.fileName = this.file.name;
this.groupName = this.group.get('title');
this.groupName = this.group.title;
this.title = this._getFieldString('Title');
this.password = entry.fields.Password || kdbxweb.ProtectedValue.fromString('');
this.notes = this._getFieldString('Notes');
@ -71,7 +68,7 @@ const EntryModel = Backbone.Model.extend({
if (this.hasFieldRefs) {
this.resolveFieldReferences();
}
},
}
_getFieldString(field) {
const val = this.entry.fields[field];
@ -82,7 +79,7 @@ const EntryModel = Backbone.Model.extend({
return val.getText();
}
return val.toString();
},
}
_checkUpdatedEntry() {
if (this.isJustCreated) {
@ -94,7 +91,7 @@ const EntryModel = Backbone.Model.extend({
if (this.unsaved && +this.updated !== +this.entry.times.lastModTime) {
this.unsaved = false;
}
},
}
_buildSearchText() {
let text = '';
@ -110,7 +107,7 @@ const EntryModel = Backbone.Model.extend({
text += att.title.toLowerCase() + '\n';
});
this.searchText = text;
},
}
_buildCustomIcon() {
this.customIcon = null;
@ -121,15 +118,15 @@ const EntryModel = Backbone.Model.extend({
);
this.customIconId = this.entry.customIcon.toString();
}
},
}
_buildSearchTags() {
this.searchTags = this.entry.tags.map(tag => tag.toLowerCase());
},
}
_buildSearchColor() {
this.searchColor = this.color;
},
}
_buildAutoType() {
this.autoTypeEnabled = this.entry.autoType.enabled;
@ -138,30 +135,30 @@ const EntryModel = Backbone.Model.extend({
kdbxweb.Consts.AutoTypeObfuscationOptions.UseClipboard;
this.autoTypeSequence = this.entry.autoType.defaultSequence;
this.autoTypeWindows = this.entry.autoType.items.map(this._convertAutoTypeItem);
},
}
_convertAutoTypeItem(item) {
return { window: item.window, sequence: item.keystrokeSequence };
},
}
_iconFromId(id) {
return IconMap[id];
},
}
_getDisplayUrl(url) {
if (!url) {
return '';
}
return url.replace(this.urlRegex, '');
},
return url.replace(UrlRegex, '');
}
_colorToModel(color) {
return color ? Color.getNearest(color) : null;
},
}
_fieldsToModel(fields) {
return omit(fields, this.builtInFields);
},
}
_attachmentsToModel(binaries) {
const att = [];
@ -174,7 +171,7 @@ const EntryModel = Backbone.Model.extend({
}
}
return att;
},
}
_entryModified() {
if (!this.unsaved) {
@ -186,7 +183,7 @@ const EntryModel = Backbone.Model.extend({
this.isJustCreated = false;
}
this.entry.times.update();
},
}
setSaved() {
if (this.unsaved) {
@ -195,7 +192,7 @@ const EntryModel = Backbone.Model.extend({
if (this.canBeDeleted) {
this.canBeDeleted = false;
}
},
}
matches(filter) {
return (
@ -210,7 +207,7 @@ const EntryModel = Backbone.Model.extend({
this.searchColor === filter.color) &&
(!filter.autoType || this.autoTypeEnabled))
);
},
}
matchesAdv(filter) {
const adv = filter.advanced;
@ -240,28 +237,28 @@ const EntryModel = Backbone.Model.extend({
}
}
return false;
},
}
matchString(str, find) {
if (str.isProtected) {
return str.includes(find);
}
return str.indexOf(find) >= 0;
},
}
matchStringLower(str, findLower) {
if (str.isProtected) {
return str.includesLower(findLower);
}
return str.toLowerCase().indexOf(findLower) >= 0;
},
}
matchRegex(str, regex) {
if (str.isProtected) {
str = str.getText();
}
return regex.test(str);
},
}
matchEntry(entry, adv, compare, search) {
const matchField = this.matchField;
@ -282,10 +279,9 @@ const EntryModel = Backbone.Model.extend({
}
let matches = false;
if (adv.other || adv.protect) {
const builtInFields = this.builtInFields;
const fieldNames = Object.keys(entry.fields);
matches = fieldNames.some(field => {
if (builtInFields.indexOf(field) >= 0) {
if (BuiltInFields.indexOf(field) >= 0) {
return false;
}
if (typeof entry.fields[field] === 'string') {
@ -296,16 +292,16 @@ const EntryModel = Backbone.Model.extend({
});
}
return matches;
},
}
matchField(entry, field, compare, search) {
const val = entry.fields[field];
return val ? compare(val, search) : false;
},
}
resolveFieldReferences() {
this.hasFieldRefs = false;
this.fieldRefFields.forEach(field => {
FieldRefFields.forEach(field => {
const fieldValue = this[field];
const refValue = this._resolveFieldReference(fieldValue);
if (refValue !== undefined) {
@ -313,7 +309,7 @@ const EntryModel = Backbone.Model.extend({
this.hasFieldRefs = true;
}
});
},
}
getFieldValue(field) {
field = field.toLowerCase();
@ -333,7 +329,7 @@ const EntryModel = Backbone.Model.extend({
}
return fieldValue;
}
},
}
_resolveFieldReference(fieldValue) {
if (!fieldValue) {
@ -345,12 +341,12 @@ const EntryModel = Backbone.Model.extend({
if (typeof fieldValue !== 'string') {
return;
}
const match = fieldValue.match(this.fieldRefRegex);
const match = fieldValue.match(FieldRefRegex);
if (!match) {
return;
}
return this._getReferenceValue(match[1], match[2]);
},
}
_getReferenceValue(fieldRefId, idStr) {
const id = new Uint8Array(16);
@ -362,40 +358,40 @@ const EntryModel = Backbone.Model.extend({
if (!entry) {
return;
}
return entry.entry.fields[this.fieldRefIds[fieldRefId]];
},
return entry.entry.fields[FieldRefIds[fieldRefId]];
}
setColor(color) {
this._entryModified();
this.entry.bgColor = Color.getKnownBgColor(color);
this._fillByEntry();
},
}
setIcon(iconId) {
this._entryModified();
this.entry.icon = iconId;
this.entry.customIcon = undefined;
this._fillByEntry();
},
}
setCustomIcon(customIconId) {
this._entryModified();
this.entry.customIcon = new kdbxweb.KdbxUuid(customIconId);
this._fillByEntry();
},
}
setExpires(dt) {
this._entryModified();
this.entry.times.expiryTime = dt instanceof Date ? dt : undefined;
this.entry.times.expires = !!dt;
this._fillByEntry();
},
}
setTags(tags) {
this._entryModified();
this.entry.tags = tags;
this._fillByEntry();
},
}
renameTag(from, to) {
const ix = this.entry.tags.findIndex(tag => tag.toLowerCase() === from.toLowerCase());
@ -408,11 +404,11 @@ const EntryModel = Backbone.Model.extend({
this.entry.tags.push(to);
}
this._fillByEntry();
},
}
setField(field, val, allowEmpty) {
const hasValue = val && (typeof val === 'string' || (val.isProtected && val.byteLength));
if (hasValue || allowEmpty || this.builtInFields.indexOf(field) >= 0) {
if (hasValue || allowEmpty || BuiltInFields.indexOf(field) >= 0) {
this._entryModified();
val = this.sanitizeFieldValue(val);
this.entry.fields[field] = val;
@ -421,7 +417,7 @@ const EntryModel = Backbone.Model.extend({
delete this.entry.fields[field];
}
this._fillByEntry();
},
}
sanitizeFieldValue(val) {
if (val && !val.isProtected && val.indexOf('\x1A') >= 0) {
@ -430,11 +426,11 @@ const EntryModel = Backbone.Model.extend({
val = val.replace(/\x1A/g, '');
}
return val;
},
}
hasField(field) {
return Object.prototype.hasOwnProperty.call(this.entry.fields, field);
},
}
addAttachment(name, data) {
this._entryModified();
@ -442,13 +438,13 @@ const EntryModel = Backbone.Model.extend({
this.entry.binaries[name] = binaryRef;
this._fillByEntry();
});
},
}
removeAttachment(name) {
this._entryModified();
delete this.entry.binaries[name];
this._fillByEntry();
},
}
getHistory() {
const history = this.entry.history.map(function(rec) {
@ -457,7 +453,7 @@ const EntryModel = Backbone.Model.extend({
history.push(this);
history.sort((x, y) => x.updated - y.updated);
return history;
},
}
deleteHistory(historyEntry) {
const ix = this.entry.history.indexOf(historyEntry);
@ -466,7 +462,7 @@ const EntryModel = Backbone.Model.extend({
this.file.setModified();
}
this._fillByEntry();
},
}
revertToHistoryState(historyEntry) {
const ix = this.entry.history.indexOf(historyEntry);
@ -481,7 +477,7 @@ const EntryModel = Backbone.Model.extend({
this.entry.copyFrom(historyEntry);
this._entryModified();
this._fillByEntry();
},
}
discardUnsaved() {
if (this.unsaved && this.entry.history.length) {
@ -493,7 +489,7 @@ const EntryModel = Backbone.Model.extend({
this.entry.copyFrom(historyEntry);
this._fillByEntry();
}
},
}
moveToTrash() {
this.file.setModified();
@ -502,13 +498,13 @@ const EntryModel = Backbone.Model.extend({
}
this.file.db.remove(this.entry);
this.file.reload();
},
}
deleteFromTrash() {
this.file.setModified();
this.file.db.move(this.entry, null);
this.file.reload();
},
}
removeWithoutHistory() {
if (this.canBeDeleted) {
@ -518,12 +514,12 @@ const EntryModel = Backbone.Model.extend({
}
this.file.reload();
}
},
}
moveToFile(file) {
if (this.canBeDeleted) {
this.removeWithoutHistory();
this.group = file.groups.first();
this.group = file.groups[0];
this.file = file;
this._fillByEntry();
this.entry.times.update();
@ -533,7 +529,7 @@ const EntryModel = Backbone.Model.extend({
this.unsaved = true;
this.file.setModified();
}
},
}
initOtpGenerator() {
let otpUrl;
@ -592,35 +588,35 @@ const EntryModel = Backbone.Model.extend({
} else {
this.otpGenerator = null;
}
},
}
setOtp(otp) {
this.otpGenerator = otp;
this.setOtpUrl(otp.url);
},
}
setOtpUrl(url) {
this.setField('otp', url ? kdbxweb.ProtectedValue.fromString(url) : undefined);
delete this.entry.fields['TOTP Seed'];
delete this.entry.fields['TOTP Settings'];
},
}
getEffectiveEnableAutoType() {
if (typeof this.entry.autoType.enabled === 'boolean') {
return this.entry.autoType.enabled;
}
return this.group.getEffectiveEnableAutoType();
},
}
getEffectiveAutoTypeSeq() {
return this.entry.autoType.defaultSequence || this.group.getEffectiveAutoTypeSeq();
},
}
setEnableAutoType(enabled) {
this._entryModified();
this.entry.autoType.enabled = enabled;
this._buildAutoType();
},
}
setAutoTypeObfuscation(enabled) {
this._entryModified();
@ -628,23 +624,23 @@ const EntryModel = Backbone.Model.extend({
? kdbxweb.Consts.AutoTypeObfuscationOptions.UseClipboard
: kdbxweb.Consts.AutoTypeObfuscationOptions.None;
this._buildAutoType();
},
}
setAutoTypeSeq(seq) {
this._entryModified();
this.entry.autoType.defaultSequence = seq || undefined;
this._buildAutoType();
},
}
getGroupPath() {
let group = this.group;
const groupPath = [];
while (group) {
groupPath.unshift(group.get('title'));
groupPath.unshift(group.title);
group = group.parentGroup;
}
return groupPath;
},
}
cloneEntry(nameSuffix) {
const newEntry = EntryModel.newEntry(this.group, this.file);
@ -657,7 +653,7 @@ const EntryModel = Backbone.Model.extend({
newEntry._fillByEntry();
this.file.reload();
return newEntry;
},
}
copyFromTemplate(templateEntry) {
const uuid = this.entry.uuid;
@ -667,7 +663,7 @@ const EntryModel = Backbone.Model.extend({
this.entry.times.creationTime = this.entry.times.lastModTime;
this.entry.fields.Title = '';
this._fillByEntry();
},
}
getRank(filter) {
const searchString = filter.textLower;
@ -702,30 +698,32 @@ const EntryModel = Backbone.Model.extend({
const fieldWeight = fieldWeights[fieldName] || defaultFieldWeight;
return rank + stringRank * fieldWeight;
}, 0);
},
}
getHtml() {
return KdbxToHtml.entryToHtml(this.file.db, this.entry);
}
});
EntryModel.fromEntry = function(entry, group, file) {
const model = new EntryModel();
model.setEntry(entry, group, file);
return model;
};
static fromEntry(entry, group, file) {
const model = new EntryModel();
model.setEntry(entry, group, file);
return model;
}
EntryModel.newEntry = function(group, file) {
const model = new EntryModel();
const entry = file.db.createEntry(group.group);
model.setEntry(entry, group, file);
model.entry.times.update();
model.unsaved = true;
model.isJustCreated = true;
model.canBeDeleted = true;
group.addEntry(model);
file.setModified();
return model;
};
static newEntry(group, file) {
const model = new EntryModel();
const entry = file.db.createEntry(group.group);
model.setEntry(entry, group, file);
model.entry.times.update();
model.unsaved = true;
model.isJustCreated = true;
model.canBeDeleted = true;
group.addEntry(model);
file.setModified();
return model;
}
}
EntryModel.defineModelProperties({}, { extensions: true });
export { EntryModel };

View File

@ -172,7 +172,7 @@ class FileModel extends Model {
} else {
groupModel = GroupModel.fromGroup(group, this);
}
groups.add(groupModel);
groups.push(groupModel);
}, this);
this.buildObjectMap();
this.resolveFieldReferences();
@ -384,7 +384,7 @@ class FileModel extends Model {
}
createEntryTemplatesGroup() {
const rootGroup = this.groups.first();
const rootGroup = this.groups[0];
const templatesGroup = GroupModel.newGroup(rootGroup, this);
templatesGroup.setName('Templates');
this.db.meta.entryTemplatesGroup = templatesGroup.group.uuid;
@ -553,7 +553,7 @@ class FileModel extends Model {
this.db.meta.name = name;
this.db.meta.nameChanged = new Date();
this.name = name;
this.groups.first().setName(name);
this.groups[0].setName(name);
this.setModified();
this.reload();
}
@ -630,8 +630,8 @@ class FileModel extends Model {
this.db.move(entry, null);
modified = true;
}, this);
trashGroup.get('items').reset();
trashGroup.get('entries').reset();
trashGroup.items.length = 0;
trashGroup.entries.length = 0;
if (modified) {
this.setModified();
}

View File

@ -10,20 +10,7 @@ const KdbxIcons = kdbxweb.Consts.Icons;
const DefaultAutoTypeSequence = '{USERNAME}{TAB}{PASSWORD}{ENTER}';
const GroupModel = MenuItemModel.extend({
defaults: Object.assign({}, MenuItemModel.prototype.defaults, {
iconId: 0,
entries: null,
filterKey: 'group',
editable: true,
top: false,
drag: true,
drop: true,
enableSearching: true,
enableAutoType: null,
autoTypeSeq: null
}),
class GroupModel extends MenuItemModel {
setGroup(group, file, parentGroup) {
const isRecycleBin = group.uuid.equals(file.db.meta.recycleBinUuid);
const id = file.subId(group.uuid.id);
@ -49,8 +36,8 @@ const GroupModel = MenuItemModel.extend({
this.file = file;
this.parentGroup = parentGroup;
this._fillByGroup(true);
const items = this.get('items');
const entries = this.get('entries');
const items = this.items;
const entries = this.entries;
const itemsArray = group.groups.map(subGroup => {
let g = file.getGroup(file.subId(subGroup.uuid.id));
@ -61,7 +48,7 @@ const GroupModel = MenuItemModel.extend({
}
return g;
}, this);
items.add(itemsArray);
items.push(...itemsArray);
const entriesArray = group.entries.map(entry => {
let e = file.getEntry(file.subId(entry.uuid.id));
@ -72,8 +59,8 @@ const GroupModel = MenuItemModel.extend({
}
return e;
}, this);
entries.add(entriesArray);
},
entries.push(...entriesArray);
}
_fillByGroup(silent) {
this.set(
@ -87,14 +74,14 @@ const GroupModel = MenuItemModel.extend({
},
{ silent }
);
},
}
_iconFromId(id) {
if (id === KdbxIcons.Folder || id === KdbxIcons.FolderOpen) {
return undefined;
}
return IconMap[id];
},
}
_buildCustomIcon() {
this.customIcon = null;
@ -102,7 +89,7 @@ const GroupModel = MenuItemModel.extend({
return IconUrlFormat.toDataUrl(this.file.db.meta.customIcons[this.group.customIcon]);
}
return null;
},
}
_groupModified() {
if (this.isJustCreated) {
@ -110,26 +97,26 @@ const GroupModel = MenuItemModel.extend({
}
this.file.setModified();
this.group.times.update();
},
}
forEachGroup(callback, filter) {
let result = true;
this.get('items').forEach(group => {
this.items.forEach(group => {
if (group.matches(filter)) {
result =
callback(group) !== false && group.forEachGroup(callback, filter) !== false;
}
});
return result;
},
}
forEachOwnEntry(filter, callback) {
this.get('entries').forEach(function(entry) {
this.entries.forEach(function(entry) {
if (entry.matches(filter)) {
callback(entry, this);
}
});
},
}
matches(filter) {
return (
@ -138,52 +125,52 @@ const GroupModel = MenuItemModel.extend({
!this.group.uuid.equals(this.file.db.meta.entryTemplatesGroup))) &&
(!filter || !filter.autoType || this.group.enableAutoType !== false)
);
},
}
getOwnSubGroups() {
return this.group.groups;
},
}
addEntry(entry) {
this.get('entries').add(entry);
},
this.entries.push(entry);
}
addGroup(group) {
this.get('items').add(group);
},
this.items.push(group);
}
setName(name) {
this._groupModified();
this.group.name = name;
this._fillByGroup();
},
}
setIcon(iconId) {
this._groupModified();
this.group.icon = iconId;
this.group.customIcon = undefined;
this._fillByGroup();
},
}
setCustomIcon(customIconId) {
this._groupModified();
this.group.customIcon = new kdbxweb.KdbxUuid(customIconId);
this._fillByGroup();
},
}
setExpanded(expanded) {
// this._groupModified(); // it's not good to mark the file as modified when a group is collapsed
this.group.expanded = expanded;
this.set('expanded', expanded);
},
this.expanded = expanded;
}
setEnableSearching(enabled) {
this._groupModified();
let parentEnableSearching = true;
let parentGroup = this.parentGroup;
while (parentGroup) {
if (typeof parentGroup.get('enableSearching') === 'boolean') {
parentEnableSearching = parentGroup.get('enableSearching');
if (typeof parentGroup.enableSearching === 'boolean') {
parentEnableSearching = parentGroup.enableSearching;
break;
}
parentGroup = parentGroup.parentGroup;
@ -192,27 +179,27 @@ const GroupModel = MenuItemModel.extend({
enabled = null;
}
this.group.enableSearching = enabled;
this.set('enableSearching', this.group.enableSearching);
},
this.enableSearching = this.group.enableSearching;
}
getEffectiveEnableSearching() {
let grp = this;
while (grp) {
if (typeof grp.get('enableSearching') === 'boolean') {
return grp.get('enableSearching');
if (typeof grp.enableSearching === 'boolean') {
return grp.enableSearching;
}
grp = grp.parentGroup;
}
return true;
},
}
setEnableAutoType(enabled) {
this._groupModified();
let parentEnableAutoType = true;
let parentGroup = this.parentGroup;
while (parentGroup) {
if (typeof parentGroup.get('enableAutoType') === 'boolean') {
parentEnableAutoType = parentGroup.get('enableAutoType');
if (typeof parentGroup.enableAutoType === 'boolean') {
parentEnableAutoType = parentGroup.enableAutoType;
break;
}
parentGroup = parentGroup.parentGroup;
@ -221,46 +208,46 @@ const GroupModel = MenuItemModel.extend({
enabled = null;
}
this.group.enableAutoType = enabled;
this.set('enableAutoType', this.group.enableAutoType);
},
this.enableAutoType = this.group.enableAutoType;
}
getEffectiveEnableAutoType() {
let grp = this;
while (grp) {
if (typeof grp.get('enableAutoType') === 'boolean') {
return grp.get('enableAutoType');
if (typeof grp.enableAutoType === 'boolean') {
return grp.enableAutoType;
}
grp = grp.parentGroup;
}
return true;
},
}
setAutoTypeSeq(seq) {
this._groupModified();
this.group.defaultAutoTypeSeq = seq || undefined;
this.set('autoTypeSeq', this.group.defaultAutoTypeSeq);
},
this.autoTypeSeq = this.group.defaultAutoTypeSeq;
}
getEffectiveAutoTypeSeq() {
let grp = this;
while (grp) {
if (grp.get('autoTypeSeq')) {
return grp.get('autoTypeSeq');
if (grp.autoTypeSeq) {
return grp.autoTypeSeq;
}
grp = grp.parentGroup;
}
return DefaultAutoTypeSequence;
},
}
getParentEffectiveAutoTypeSeq() {
return this.parentGroup
? this.parentGroup.getEffectiveAutoTypeSeq()
: DefaultAutoTypeSequence;
},
}
isEntryTemplatesGroup() {
return this.group.uuid.equals(this.file.db.meta.entryTemplatesGroup);
},
}
moveToTrash() {
this.file.setModified();
@ -269,12 +256,12 @@ const GroupModel = MenuItemModel.extend({
this.file.db.meta.entryTemplatesGroup = undefined;
}
this.file.reload();
},
}
deleteFromTrash() {
this.file.db.move(this.group, null);
this.file.reload();
},
}
removeWithoutHistory() {
const ix = this.parentGroup.group.groups.indexOf(this.group);
@ -282,7 +269,7 @@ const GroupModel = MenuItemModel.extend({
this.parentGroup.group.groups.splice(ix, 1);
}
this.file.reload();
},
}
moveHere(object) {
if (!object || object.id === this.id || object.file !== this.file) {
@ -307,7 +294,7 @@ const GroupModel = MenuItemModel.extend({
this.file.db.move(object.entry, this.group);
this.file.reload();
}
},
}
moveToTop(object) {
if (
@ -334,24 +321,37 @@ const GroupModel = MenuItemModel.extend({
}
this.file.reload();
}
static fromGroup(group, file, parentGroup) {
const model = new GroupModel();
model.setGroup(group, file, parentGroup);
return model;
}
static newGroup(group, file) {
const model = new GroupModel();
const grp = file.db.createGroup(group.group);
model.setGroup(grp, file, group);
model.group.times.update();
model.isJustCreated = true;
group.addGroup(model);
file.setModified();
file.reload();
return model;
}
}
GroupModel.defineModelProperties({
iconId: 0,
entries: null,
filterKey: 'group',
editable: true,
top: false,
drag: true,
drop: true,
enableSearching: true,
enableAutoType: null,
autoTypeSeq: null
});
GroupModel.fromGroup = function(group, file, parentGroup) {
const model = new GroupModel();
model.setGroup(group, file, parentGroup);
return model;
};
GroupModel.newGroup = function(group, file) {
const model = new GroupModel();
const grp = file.db.createGroup(group.group);
model.setGroup(grp, file, group);
model.group.times.update();
model.isJustCreated = true;
group.addGroup(model);
file.setModified();
file.reload();
return model;
};
export { GroupModel };

View File

@ -1,19 +1,15 @@
import { GroupCollection } from 'collections/group-collection';
import { MenuSectionModel } from 'models/menu/menu-section-model';
const GroupsMenuModel = MenuSectionModel.extend({
defaults: Object.assign({}, MenuSectionModel.prototype.defaults, {
scrollable: true,
grow: true
}),
initialize() {
this.set('items', new GroupCollection());
},
_loadItemCollectionType() {