mirror of https://github.com/keeweb/keeweb.git
fix #112: entry templates
This commit is contained in:
parent
5f2eea2c29
commit
0bb7602bf8
|
@ -17,6 +17,8 @@
|
||||||
"group": "group",
|
"group": "group",
|
||||||
"noTitle": "no title",
|
"noTitle": "no title",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
|
"history": "history",
|
||||||
|
"template": "template",
|
||||||
"notImplemented": "Not Implemented",
|
"notImplemented": "Not Implemented",
|
||||||
"saveChanges": "Save changes",
|
"saveChanges": "Save changes",
|
||||||
"discardChanges": "Discard changes",
|
"discardChanges": "Discard changes",
|
||||||
|
@ -25,7 +27,6 @@
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"plugins": "Plugins",
|
"plugins": "Plugins",
|
||||||
"history": "history",
|
|
||||||
|
|
||||||
"cache": "cache",
|
"cache": "cache",
|
||||||
"file": "file",
|
"file": "file",
|
||||||
|
|
|
@ -313,9 +313,30 @@ const AppModel = Backbone.Model.extend({
|
||||||
return matches.map(m => m[0]);
|
return matches.map(m => m[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
createNewEntry: function() {
|
getEntryTemplates: function() {
|
||||||
|
const entryTemplates = [];
|
||||||
|
this.files.forEach(file => {
|
||||||
|
file.forEachEntryTemplate(entry => {
|
||||||
|
entryTemplates.push({ file, entry });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return entryTemplates;
|
||||||
|
},
|
||||||
|
|
||||||
|
createNewEntry: function(args) {
|
||||||
const sel = this.getFirstSelectedGroup();
|
const sel = this.getFirstSelectedGroup();
|
||||||
return EntryModel.newEntry(sel.group, sel.file);
|
if (args && args.template) {
|
||||||
|
if (sel.file !== args.template.file) {
|
||||||
|
sel.file = args.template.file;
|
||||||
|
sel.group = args.template.file.get('groups').first();
|
||||||
|
}
|
||||||
|
const templateEntry = args.template.entry;
|
||||||
|
const newEntry = EntryModel.newEntry(sel.group, sel.file);
|
||||||
|
newEntry.copyFromTemplate(templateEntry);
|
||||||
|
return newEntry;
|
||||||
|
} else {
|
||||||
|
return EntryModel.newEntry(sel.group, sel.file);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createNewGroup: function() {
|
createNewGroup: function() {
|
||||||
|
@ -724,27 +745,6 @@ const AppModel = Backbone.Model.extend({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const saveToCacheAndStorage = () => {
|
|
||||||
logger.info('Getting file data for saving');
|
|
||||||
file.getData((data, err) => {
|
|
||||||
if (err) { return complete(err); }
|
|
||||||
if (storage === 'file') {
|
|
||||||
logger.info('Saving to file storage');
|
|
||||||
saveToStorage(data);
|
|
||||||
} else if (!file.get('dirty')) {
|
|
||||||
logger.info('Saving to storage, skip cache because not dirty');
|
|
||||||
saveToStorage(data);
|
|
||||||
} else {
|
|
||||||
logger.info('Saving to cache');
|
|
||||||
Storage.cache.save(fileInfo.id, null, data, (err) => {
|
|
||||||
if (err) { return complete(err); }
|
|
||||||
file.set('dirty', false);
|
|
||||||
logger.info('Saved to cache, saving to storage');
|
|
||||||
saveToStorage(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const saveToStorage = (data) => {
|
const saveToStorage = (data) => {
|
||||||
logger.info('Save data to storage');
|
logger.info('Save data to storage');
|
||||||
Storage[storage].save(path, opts, data, (err, stat) => {
|
Storage[storage].save(path, opts, data, (err, stat) => {
|
||||||
|
@ -772,6 +772,27 @@ const AppModel = Backbone.Model.extend({
|
||||||
}
|
}
|
||||||
}, fileInfo.get('rev'));
|
}, fileInfo.get('rev'));
|
||||||
};
|
};
|
||||||
|
const saveToCacheAndStorage = () => {
|
||||||
|
logger.info('Getting file data for saving');
|
||||||
|
file.getData((data, err) => {
|
||||||
|
if (err) { return complete(err); }
|
||||||
|
if (storage === 'file') {
|
||||||
|
logger.info('Saving to file storage');
|
||||||
|
saveToStorage(data);
|
||||||
|
} else if (!file.get('dirty')) {
|
||||||
|
logger.info('Saving to storage, skip cache because not dirty');
|
||||||
|
saveToStorage(data);
|
||||||
|
} else {
|
||||||
|
logger.info('Saving to cache');
|
||||||
|
Storage.cache.save(fileInfo.id, null, data, (err) => {
|
||||||
|
if (err) { return complete(err); }
|
||||||
|
file.set('dirty', false);
|
||||||
|
logger.info('Saved to cache, saving to storage');
|
||||||
|
saveToStorage(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
logger.info('Stat file');
|
logger.info('Stat file');
|
||||||
Storage[storage].stat(path, opts, (err, stat) => {
|
Storage[storage].stat(path, opts, (err, stat) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -622,6 +622,12 @@ const EntryModel = Backbone.Model.extend({
|
||||||
newEntry._fillByEntry();
|
newEntry._fillByEntry();
|
||||||
this.file.reload();
|
this.file.reload();
|
||||||
return newEntry;
|
return newEntry;
|
||||||
|
},
|
||||||
|
|
||||||
|
copyFromTemplate: function(templateEntry) {
|
||||||
|
this.entry.copyFrom(templateEntry.entry);
|
||||||
|
this.entry.fields.Title = '';
|
||||||
|
this._fillByEntry();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -377,6 +377,17 @@ const FileModel = Backbone.Model.extend({
|
||||||
return hash ? kdbxweb.ByteUtils.bytesToBase64(hash.getBinary()) : null;
|
return hash ? kdbxweb.ByteUtils.bytesToBase64(hash.getBinary()) : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
forEachEntryTemplate: function(callback) {
|
||||||
|
if (!this.db.meta.entryTemplatesGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const group = this.getGroup(this.subId(this.db.meta.entryTemplatesGroup.id));
|
||||||
|
if (!group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
group.forEachOwnEntry({}, callback);
|
||||||
|
},
|
||||||
|
|
||||||
setSyncProgress: function() {
|
setSyncProgress: function() {
|
||||||
this.set({ syncing: true });
|
this.set({ syncing: true });
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,7 +29,7 @@ const GroupModel = MenuItemModel.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setGroup: function(group, file, parentGroup) {
|
setGroup: function(group, file, parentGroup) {
|
||||||
const isRecycleBin = file.db.meta.recycleBinUuid && file.db.meta.recycleBinUuid.id === group.uuid.id;
|
const isRecycleBin = group.uuid.equals(file.db.meta.recycleBinUuid);
|
||||||
const id = file.subId(group.uuid.id);
|
const id = file.subId(group.uuid.id);
|
||||||
this.set({
|
this.set({
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -125,7 +125,10 @@ const GroupModel = MenuItemModel.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
matches: function(filter) {
|
matches: function(filter) {
|
||||||
return (filter && filter.includeDisabled || this.group.enableSearching !== false) &&
|
return (filter && filter.includeDisabled ||
|
||||||
|
this.group.enableSearching !== false &&
|
||||||
|
!this.group.uuid.equals(this.file.db.meta.entryTemplatesGroup)
|
||||||
|
) &&
|
||||||
(!filter || !filter.autoType || this.group.enableAutoType !== false);
|
(!filter || !filter.autoType || this.group.enableAutoType !== false);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ const DropdownView = require('./dropdown-view');
|
||||||
const FeatureDetector = require('../util/feature-detector');
|
const FeatureDetector = require('../util/feature-detector');
|
||||||
const Format = require('../util/format');
|
const Format = require('../util/format');
|
||||||
const Locale = require('../util/locale');
|
const Locale = require('../util/locale');
|
||||||
|
const Comparators = require('../util/comparators');
|
||||||
|
|
||||||
const ListSearchView = Backbone.View.extend({
|
const ListSearchView = Backbone.View.extend({
|
||||||
template: require('templates/list-search.hbs'),
|
template: require('templates/list-search.hbs'),
|
||||||
|
@ -278,6 +279,7 @@ const ListSearchView = Backbone.View.extend({
|
||||||
this.hideSearchOptions();
|
this.hideSearchOptions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hideSearchOptions();
|
this.hideSearchOptions();
|
||||||
this.$el.find('.list__search-btn-new').addClass('sel--active');
|
this.$el.find('.list__search-btn-new').addClass('sel--active');
|
||||||
const view = new DropdownView();
|
const view = new DropdownView();
|
||||||
|
@ -288,11 +290,30 @@ const ListSearchView = Backbone.View.extend({
|
||||||
top: this.$el.find('.list__search-btn-new')[0].getBoundingClientRect().bottom,
|
top: this.$el.find('.list__search-btn-new')[0].getBoundingClientRect().bottom,
|
||||||
right: this.$el[0].getBoundingClientRect().right + 1
|
right: this.$el[0].getBoundingClientRect().right + 1
|
||||||
},
|
},
|
||||||
options: this.createOptions
|
options: this.createOptions.concat(this.getCreateEntryTemplateOptions())
|
||||||
});
|
});
|
||||||
this.views.searchDropdown = view;
|
this.views.searchDropdown = view;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCreateEntryTemplateOptions: function() {
|
||||||
|
const entryTemplates = this.model.getEntryTemplates();
|
||||||
|
const hasMultipleFiles = this.model.files.length > 1;
|
||||||
|
this.entryTemplates = {};
|
||||||
|
const options = [];
|
||||||
|
entryTemplates.forEach(tmpl => {
|
||||||
|
const id = 'tmpl:' + tmpl.entry.id;
|
||||||
|
options.push({
|
||||||
|
value: id,
|
||||||
|
icon: tmpl.entry.icon,
|
||||||
|
text: hasMultipleFiles ? tmpl.file.get('name') + ' / ' + tmpl.entry.title : tmpl.entry.title
|
||||||
|
});
|
||||||
|
this.entryTemplates[id] = tmpl;
|
||||||
|
});
|
||||||
|
options.sort(Comparators.stringComparator('text', true));
|
||||||
|
options.push({ value: 'tmpl', icon: 'sticky-note-o', text: Format.capFirst(Locale.template) });
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
|
||||||
sortDropdownSelect: function(e) {
|
sortDropdownSelect: function(e) {
|
||||||
this.hideSearchOptions();
|
this.hideSearchOptions();
|
||||||
Backbone.trigger('set-sort', e.item);
|
Backbone.trigger('set-sort', e.item);
|
||||||
|
@ -307,6 +328,13 @@ const ListSearchView = Backbone.View.extend({
|
||||||
case 'group':
|
case 'group':
|
||||||
this.trigger('create-group');
|
this.trigger('create-group');
|
||||||
break;
|
break;
|
||||||
|
case 'tmpl':
|
||||||
|
this.trigger('create-template');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (this.entryTemplates[e.item]) {
|
||||||
|
this.trigger('create-entry', { template: this.entryTemplates[e.item] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ const DragDropInfo = require('../comp/drag-drop-info');
|
||||||
const AppSettingsModel = require('../models/app-settings-model');
|
const AppSettingsModel = require('../models/app-settings-model');
|
||||||
const Locale = require('../util/locale');
|
const Locale = require('../util/locale');
|
||||||
const Format = require('../util/format');
|
const Format = require('../util/format');
|
||||||
|
const Alerts = require('../comp/alerts');
|
||||||
|
|
||||||
const ListView = Backbone.View.extend({
|
const ListView = Backbone.View.extend({
|
||||||
template: require('templates/list.hbs'),
|
template: require('templates/list.hbs'),
|
||||||
|
@ -47,6 +48,7 @@ const ListView = Backbone.View.extend({
|
||||||
this.listenTo(this.views.search, 'select-next', this.selectNext);
|
this.listenTo(this.views.search, 'select-next', this.selectNext);
|
||||||
this.listenTo(this.views.search, 'create-entry', this.createEntry);
|
this.listenTo(this.views.search, 'create-entry', this.createEntry);
|
||||||
this.listenTo(this.views.search, 'create-group', this.createGroup);
|
this.listenTo(this.views.search, 'create-group', this.createGroup);
|
||||||
|
this.listenTo(this.views.search, 'create-template', this.createTemplate);
|
||||||
this.listenTo(this, 'show', this.viewShown);
|
this.listenTo(this, 'show', this.viewShown);
|
||||||
this.listenTo(this, 'hide', this.viewHidden);
|
this.listenTo(this, 'hide', this.viewHidden);
|
||||||
this.listenTo(this, 'view-resize', this.viewResized);
|
this.listenTo(this, 'view-resize', this.viewResized);
|
||||||
|
@ -147,8 +149,8 @@ const ListView = Backbone.View.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createEntry: function() {
|
createEntry: function(arg) {
|
||||||
const newEntry = this.model.createNewEntry();
|
const newEntry = this.model.createNewEntry(arg);
|
||||||
this.items.unshift(newEntry);
|
this.items.unshift(newEntry);
|
||||||
this.render();
|
this.render();
|
||||||
this.selectItem(newEntry);
|
this.selectItem(newEntry);
|
||||||
|
@ -159,6 +161,10 @@ const ListView = Backbone.View.extend({
|
||||||
Backbone.trigger('edit-group', newGroup);
|
Backbone.trigger('edit-group', newGroup);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createTemplate: function() {
|
||||||
|
Alerts.notImplemented();
|
||||||
|
},
|
||||||
|
|
||||||
selectItem: function(item) {
|
selectItem: function(item) {
|
||||||
this.model.activeEntryId = item.id;
|
this.model.activeEntryId = item.id;
|
||||||
Backbone.trigger('entry-selected', item);
|
Backbone.trigger('entry-selected', item);
|
||||||
|
|
|
@ -4,6 +4,7 @@ Release notes
|
||||||
`+` plugins
|
`+` plugins
|
||||||
`*` translations are available only as plugins
|
`*` translations are available only as plugins
|
||||||
`*` Dropbox API V2
|
`*` Dropbox API V2
|
||||||
|
`+` entry templates
|
||||||
`+` support cloud providers in iOS homescreen apps
|
`+` support cloud providers in iOS homescreen apps
|
||||||
`+` mobile field editing improvements
|
`+` mobile field editing improvements
|
||||||
`+` file path hint in recent files list
|
`+` file path hint in recent files list
|
||||||
|
|
Loading…
Reference in New Issue