diff --git a/app/scripts/collections/file-collection.js b/app/scripts/collections/file-collection.js index f908c087..0e53a4ab 100644 --- a/app/scripts/collections/file-collection.js +++ b/app/scripts/collections/file-collection.js @@ -23,7 +23,7 @@ var FileCollection = Backbone.Collection.extend({ }, getById: function(id) { - return this.find(function(file) { return file.get('id') === id; }); + return this.find(function(file) { return file.id === id; }); } }); diff --git a/app/scripts/models/app-model.js b/app/scripts/models/app-model.js index bf82a4eb..8b96b2ac 100644 --- a/app/scripts/models/app-model.js +++ b/app/scripts/models/app-model.js @@ -263,7 +263,7 @@ var AppModel = Backbone.Model.extend({ createDemoFile: function() { var that = this; if (!this.files.getByName('Demo')) { - var demoFile = new FileModel(); + var demoFile = new FileModel({ id: IdGenerator.uuid() }); demoFile.openDemo(function() { that.addFile(demoFile); }); @@ -281,7 +281,7 @@ var AppModel = Backbone.Model.extend({ break; } } - var newFile = new FileModel(); + var newFile = new FileModel({ id: IdGenerator.uuid() }); newFile.create(name); this.addFile(newFile); }, @@ -383,6 +383,7 @@ var AppModel = Backbone.Model.extend({ params.keyFileData = FileModel.createKeyFileWithHash(fileInfo.get('keyFileHash')); } var file = new FileModel({ + id: fileInfo ? fileInfo.id : IdGenerator.uuid(), name: params.name, storage: params.storage, path: params.path, @@ -407,11 +408,9 @@ var AppModel = Backbone.Model.extend({ if (fileInfo) { file.set('syncDate', fileInfo.get('syncDate')); } - var cacheId = fileInfo && fileInfo.id || IdGenerator.uuid(); - file.set('cacheId', cacheId); if (updateCacheOnSuccess) { logger.info('Save loaded file to cache'); - Storage.cache.save(cacheId, null, params.fileData); + Storage.cache.save(file.id, null, params.fileData); } var rev = params.rev || fileInfo && fileInfo.get('rev'); that.setFileOpts(file, params.opts); @@ -426,6 +425,7 @@ var AppModel = Backbone.Model.extend({ var logger = new Logger('import', params.name); logger.info('File import request with supplied xml'); var file = new FileModel({ + id: IdGenerator.uuid(), name: params.name, storage: params.storage, path: params.path @@ -442,10 +442,10 @@ var AppModel = Backbone.Model.extend({ }, addToLastOpenFiles: function(file, rev) { - this.appLogger.debug('Add last open file', file.get('cacheId'), file.get('name'), file.get('storage'), file.get('path'), rev); + this.appLogger.debug('Add last open file', file.id, file.get('name'), file.get('storage'), file.get('path'), rev); var dt = new Date(); var fileInfo = new FileInfoModel({ - id: file.get('cacheId'), + id: file.id, name: file.get('name'), storage: file.get('storage'), path: file.get('path'), @@ -462,7 +462,7 @@ var AppModel = Backbone.Model.extend({ keyFileHash: file.getKeyFileHash() }); } - this.fileInfos.remove(file.get('cacheId')); + this.fileInfos.remove(file.id); this.fileInfos.unshift(fileInfo); this.fileInfos.save(); }, @@ -504,7 +504,7 @@ var AppModel = Backbone.Model.extend({ }, getFileInfo: function(file) { - return file.get('cacheId') ? this.fileInfos.get(file.get('cacheId')) : + return this.fileInfos.get(file.id) || this.fileInfos.getMatch(file.get('storage'), file.get('name'), file.get('path')); }, @@ -549,7 +549,6 @@ var AppModel = Backbone.Model.extend({ if (!err) { savedToCache = true; } logger.info('Sync finished', err || 'no error'); file.setSyncComplete(path, storage, err ? err.toString() : null, savedToCache); - file.set('cacheId', fileInfo.id); fileInfo.set({ name: file.get('name'), storage: storage, @@ -557,8 +556,7 @@ var AppModel = Backbone.Model.extend({ opts: that.getStoreOpts(file), modified: file.get('modified'), editState: file.getLocalEditState(), - syncDate: file.get('syncDate'), - cacheId: fileInfo.id + syncDate: file.get('syncDate') }); if (that.settings.get('rememberKeyFiles')) { fileInfo.set({ @@ -573,7 +571,7 @@ var AppModel = Backbone.Model.extend({ if (callback) { callback(err); } }; if (!storage) { - if (!file.get('modified') && fileInfo.id === file.get('cacheId')) { + if (!file.get('modified') && fileInfo.id === file.id) { logger.info('Local, not modified'); return complete(); } diff --git a/app/scripts/models/entry-model.js b/app/scripts/models/entry-model.js index 9f0e0180..63312a5f 100644 --- a/app/scripts/models/entry-model.js +++ b/app/scripts/models/entry-model.js @@ -21,7 +21,7 @@ var EntryModel = Backbone.Model.extend({ this.entry = entry; this.group = group; this.file = file; - if (this.id === entry.uuid.id) { + if (this.get('uuid') === entry.uuid.id) { this._checkUpdatedEntry(); } this._fillByEntry(); @@ -29,7 +29,7 @@ var EntryModel = Backbone.Model.extend({ _fillByEntry: function() { var entry = this.entry; - this.set({id: entry.uuid.id}, {silent: true}); + this.set({id: this.file.subId(entry.uuid.id), uuid: entry.uuid.id}, {silent: true}); this.fileName = this.file.get('name'); this.groupName = this.group.get('title'); this.title = entry.fields.Title || ''; diff --git a/app/scripts/models/file-model.js b/app/scripts/models/file-model.js index 2e15f973..62b7c948 100644 --- a/app/scripts/models/file-model.js +++ b/app/scripts/models/file-model.js @@ -13,6 +13,7 @@ var logger = new Logger('file'); var FileModel = Backbone.Model.extend({ defaults: { id: '', + uuid: '', name: '', keyFileName: '', passwordLength: 0, @@ -31,8 +32,7 @@ var FileModel = Backbone.Model.extend({ keyFileChanged: false, syncing: false, syncError: null, - syncDate: null, - cacheId: null + syncDate: null }, db: null, @@ -136,7 +136,7 @@ var FileModel = Backbone.Model.extend({ readModel: function() { var groups = new GroupCollection(); this.set({ - id: this.db.getDefaultGroup().uuid.toString(), + uuid: this.db.getDefaultGroup().uuid.toString(), groups: groups, defaultUser: this.db.meta.defaultUser, recycleBinEnabled: this.db.meta.recycleBinEnabled, @@ -145,18 +145,21 @@ var FileModel = Backbone.Model.extend({ keyEncryptionRounds: this.db.header.keyEncryptionRounds }, { silent: true }); this.db.groups.forEach(function(group) { - var groupModel = this.getGroup(group.uuid.id); + var groupModel = this.getGroup(this.subId(group.uuid.id)); if (groupModel) { groupModel.setGroup(group, this); } else { groupModel = GroupModel.fromGroup(group, this); } - groupModel.set({title: this.get('name')}); groups.add(groupModel); }, this); this.buildObjectMap(); }, + subId: function(id) { + return this.id + ':' + id; + }, + buildObjectMap: function() { var entryMap = {}; var groupMap = {}; @@ -257,7 +260,7 @@ var FileModel = Backbone.Model.extend({ forEachEntry: function(filter, callback) { var top = this; if (filter.trash) { - top = this.getGroup(this.db.meta.recycleBinUuid ? this.db.meta.recycleBinUuid.id : null); + top = this.getGroup(this.db.meta.recycleBinUuid ? this.subId(this.db.meta.recycleBinUuid.id) : null); } else if (filter.group) { top = this.getGroup(filter.group); } @@ -282,7 +285,7 @@ var FileModel = Backbone.Model.extend({ }, getTrashGroup: function() { - return this.db.meta.recycleBinEnabled ? this.getGroup(this.db.meta.recycleBinUuid.id) : null; + return this.db.meta.recycleBinEnabled ? this.getGroup(this.subId(this.db.meta.recycleBinUuid.id)) : null; }, setModified: function() { @@ -455,9 +458,9 @@ var FileModel = Backbone.Model.extend({ }, addCustomIcon: function(iconData) { - var id = kdbxweb.KdbxUuid.random(); - this.db.meta.customIcons[id] = kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(iconData)); - return id.toString(); + var uuid = kdbxweb.KdbxUuid.random(); + this.db.meta.customIcons[uuid] = kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(iconData)); + return uuid.toString(); }, renameTag: function(from, to) { diff --git a/app/scripts/models/group-model.js b/app/scripts/models/group-model.js index 20ef33ba..8a1179fd 100644 --- a/app/scripts/models/group-model.js +++ b/app/scripts/models/group-model.js @@ -31,13 +31,15 @@ var GroupModel = MenuItemModel.extend({ setGroup: function(group, file, parentGroup) { var isRecycleBin = file.db.meta.recycleBinUuid && file.db.meta.recycleBinUuid.id === group.uuid.id; + var id = file.subId(group.uuid.id); this.set({ - id: group.uuid.id, + id: id, + uuid: group.uuid.id, expanded: group.expanded, visible: !isRecycleBin, items: new GroupCollection(), entries: new EntryCollection(), - filterValue: group.uuid.id, + filterValue: id, enableSearching: group.enableSearching, enableAutoType: group.enableAutoType, autoTypeSeq: group.defaultAutoTypeSeq, @@ -52,7 +54,7 @@ var GroupModel = MenuItemModel.extend({ var items = this.get('items'), entries = this.get('entries'); group.groups.forEach(function(subGroup) { - var existing = file.getGroup(subGroup.uuid); + var existing = file.getGroup(file.subId(subGroup.uuid.id)); if (existing) { existing.setGroup(subGroup, file, this); items.add(existing); @@ -61,7 +63,7 @@ var GroupModel = MenuItemModel.extend({ } }, this); group.entries.forEach(function(entry) { - var existing = file.getEntry(entry.uuid); + var existing = file.getEntry(file.subId(entry.uuid.id)); if (existing) { existing.setEntry(entry, this, file); entries.add(existing); @@ -73,7 +75,7 @@ var GroupModel = MenuItemModel.extend({ _fillByGroup: function(silent) { this.set({ - title: this.group.name, + title: this.parentGroup ? this.group.name : this.file.get('name'), iconId: this.group.icon, icon: this._iconFromId(this.group.icon), customIcon: this._buildCustomIcon(), diff --git a/app/scripts/presenters/entry-presenter.js b/app/scripts/presenters/entry-presenter.js index 40ab9ef9..740cec3a 100644 --- a/app/scripts/presenters/entry-presenter.js +++ b/app/scripts/presenters/entry-presenter.js @@ -19,7 +19,7 @@ EntryPresenter.prototype = { } return this; }, - get id() { return this.entry ? this.entry.id : this.group.get('id'); }, + get id() { return this.entry ? this.entry.id : this.group.id; }, get icon() { return this.entry ? this.entry.icon : (this.group.get('icon') || 'folder'); }, get customIcon() { return this.entry ? this.entry.customIcon : undefined; }, get color() { return this.entry ? (this.entry.color || (this.entry.customIcon ? this.noColor : undefined)) : undefined; }, diff --git a/app/scripts/storage/storage-webdav.js b/app/scripts/storage/storage-webdav.js index 5b485f12..3d1a79c3 100644 --- a/app/scripts/storage/storage-webdav.js +++ b/app/scripts/storage/storage-webdav.js @@ -107,7 +107,7 @@ var StorageWebDav = StorageBase.extend({ fileOptsToStoreOpts: function(opts, file) { var result = {user: opts.user, encpass: opts.encpass}; if (opts.password) { - var fileId = file.get('id'); + var fileId = file.get('uuid'); var password = opts.password; var encpass = ''; for (var i = 0; i < password.length; i++) { @@ -121,7 +121,7 @@ var StorageWebDav = StorageBase.extend({ storeOptsToFileOpts: function(opts, file) { var result = {user: opts.user, password: opts.password}; if (opts.encpass) { - var fileId = file.get('id'); + var fileId = file.get('uuid'); var encpass = atob(opts.encpass); var password = ''; for (var i = 0; i < encpass.length; i++) { diff --git a/app/scripts/views/open-view.js b/app/scripts/views/open-view.js index cc9a751d..b984200a 100644 --- a/app/scripts/views/open-view.js +++ b/app/scripts/views/open-view.js @@ -449,6 +449,10 @@ var OpenView = Backbone.View.extend({ }, openDb: function() { + if (this.params.id && this.model.files.getById(this.params.id)) { + this.trigger('close'); + return; + } if (this.busy || !this.params.name) { return; } diff --git a/release-notes.md b/release-notes.md index f2471cca..e3047824 100644 --- a/release-notes.md +++ b/release-notes.md @@ -16,6 +16,7 @@ Auto-type, ui improvements `+` logout from remote storages on disable `*` don't check updates at startup `*` repos moved to github organization account +`*` allow opening same file twice `-` prevent second app instance on windows `-` fix drag-drop in Safari