mirror of https://github.com/keeweb/keeweb.git
file save/open
This commit is contained in:
parent
058b82f722
commit
874477c5c4
|
@ -25,7 +25,8 @@ $(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showApp() {
|
function showApp() {
|
||||||
new AppView({ model: new AppModel() }).render().showOpenFile();
|
var appModel = new AppModel();
|
||||||
|
new AppView({ model: appModel }).render().showOpenFile(appModel.settings.get('lastOpenFile'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,9 @@ var AppModel = Backbone.Model.extend({
|
||||||
page: 'file',
|
page: 'file',
|
||||||
file: file
|
file: file
|
||||||
});
|
});
|
||||||
|
if (file.get('path')) {
|
||||||
|
AppSettingsModel.instance.set('lastOpenFile', file.get('path'));
|
||||||
|
}
|
||||||
this.refresh();
|
this.refresh();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -5,19 +5,18 @@ var Backbone = require('backbone');
|
||||||
var AppSettingsModel = Backbone.Model.extend({
|
var AppSettingsModel = Backbone.Model.extend({
|
||||||
defaults: {
|
defaults: {
|
||||||
theme: 'd',
|
theme: 'd',
|
||||||
genOpts: {
|
lastOpenFile: ''
|
||||||
length: 16, upper: true, lower: true, digits: true, special: false, brackets: false, high: false, ambiguous: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
|
this.listenTo(this, 'change', this.save);
|
||||||
},
|
},
|
||||||
|
|
||||||
load: function() {
|
load: function() {
|
||||||
if (typeof localStorage !== 'undefined' && localStorage.appSettings) {
|
if (typeof localStorage !== 'undefined' && localStorage.appSettings) {
|
||||||
try {
|
try {
|
||||||
var data = JSON.parse(localStorage.appSettings);
|
var data = JSON.parse(localStorage.appSettings);
|
||||||
this.set(data);
|
this.set(data, { silent: true });
|
||||||
} catch (e) { /* failed to load settings */ }
|
} catch (e) { /* failed to load settings */ }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
var Backbone = require('backbone'),
|
var Backbone = require('backbone'),
|
||||||
GroupCollection = require('../collections/group-collection'),
|
GroupCollection = require('../collections/group-collection'),
|
||||||
GroupModel = require('./group-model'),
|
GroupModel = require('./group-model'),
|
||||||
|
Launcher = require('../util/launcher'),
|
||||||
kdbxweb = require('kdbxweb'),
|
kdbxweb = require('kdbxweb'),
|
||||||
demoFileData = require('base64!../../resources/Demo.kdbx');
|
demoFileData = require('base64!../../resources/Demo.kdbx');
|
||||||
|
|
||||||
|
@ -54,9 +55,6 @@ var FileModel = Backbone.Model.extend({
|
||||||
}
|
}
|
||||||
this.readModel(this.get('name'));
|
this.readModel(this.get('name'));
|
||||||
this.setOpenFile({ passwordLength: len });
|
this.setOpenFile({ passwordLength: len });
|
||||||
this._oldPasswordHash = this.db.credentials.passwordHash;
|
|
||||||
this._oldKeyFileHash = this.db.credentials.keyFileHash;
|
|
||||||
this._oldKeyChangeDate = this.db.meta.keyChanged;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
create: function(name) {
|
create: function(name) {
|
||||||
|
@ -82,9 +80,14 @@ var FileModel = Backbone.Model.extend({
|
||||||
opening: false,
|
opening: false,
|
||||||
error: false,
|
error: false,
|
||||||
oldKeyFileName: this.get('keyFileName'),
|
oldKeyFileName: this.get('keyFileName'),
|
||||||
oldPasswordLength: props.passwordLength
|
oldPasswordLength: props.passwordLength,
|
||||||
|
passwordChanged: false,
|
||||||
|
keyFileChanged: false
|
||||||
});
|
});
|
||||||
this.set(props);
|
this.set(props);
|
||||||
|
this._oldPasswordHash = this.db.credentials.passwordHash;
|
||||||
|
this._oldKeyFileHash = this.db.credentials.keyFileHash;
|
||||||
|
this._oldKeyChangeDate = this.db.meta.keyChanged;
|
||||||
},
|
},
|
||||||
|
|
||||||
readModel: function(topGroupTitle) {
|
readModel: function(topGroupTitle) {
|
||||||
|
@ -154,6 +157,10 @@ var FileModel = Backbone.Model.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
autoSave: function() {
|
||||||
|
Launcher.writeFile(this.get('path'), this.getData());
|
||||||
|
},
|
||||||
|
|
||||||
getData: function() {
|
getData: function() {
|
||||||
return this.db.save();
|
return this.db.save();
|
||||||
},
|
},
|
||||||
|
@ -162,6 +169,14 @@ var FileModel = Backbone.Model.extend({
|
||||||
return this.db.saveXml();
|
return this.db.saveXml();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
saved: function(path) {
|
||||||
|
this.set({ path: path || '', modified: false, created: false });
|
||||||
|
this.setOpenFile({ passwordLength: this.get('passwordLength') });
|
||||||
|
this.forEachEntry({}, function(entry) {
|
||||||
|
entry.unsaved = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
setPassword: function(password) {
|
setPassword: function(password) {
|
||||||
this.db.credentials.setPassword(password);
|
this.db.credentials.setPassword(password);
|
||||||
this.db.meta.keyChanged = new Date();
|
this.db.meta.keyChanged = new Date();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var Backbone = require('backbone');
|
||||||
var Launcher;
|
var Launcher;
|
||||||
|
|
||||||
if (window.process && window.process.versions && window.process.versions.electron) {
|
if (window.process && window.process.versions && window.process.versions.electron) {
|
||||||
|
@ -13,8 +14,36 @@ if (window.process && window.process.versions && window.process.versions.electro
|
||||||
devTools: true,
|
devTools: true,
|
||||||
openDevTools: function() {
|
openDevTools: function() {
|
||||||
this.req('remote').getCurrentWindow().openDevTools();
|
this.req('remote').getCurrentWindow().openDevTools();
|
||||||
|
},
|
||||||
|
getSaveFileName: function(defaultPath, cb) {
|
||||||
|
var remote = this.req('remote');
|
||||||
|
if (defaultPath) {
|
||||||
|
var homePath = remote.require('app').getPath('userDesktop');
|
||||||
|
defaultPath = this.req('path').join(homePath, defaultPath);
|
||||||
|
}
|
||||||
|
remote.require('dialog').showSaveDialog({
|
||||||
|
title: 'Save Passwords Database',
|
||||||
|
defaultPath: defaultPath,
|
||||||
|
filters: [{ name: 'KeePass files', extensions: ['kdbx'] }]
|
||||||
|
}, cb);
|
||||||
|
},
|
||||||
|
writeFile: function(path, data) {
|
||||||
|
this.req('fs').writeFileSync(path, new window.Buffer(data));
|
||||||
|
},
|
||||||
|
readFile: function(path) {
|
||||||
|
return new Uint8Array(this.req('fs').readFileSync(path));
|
||||||
|
},
|
||||||
|
fileExists: function(path) {
|
||||||
|
return this.req('fs').existsSync(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
window.launcherOpen = function(path) {
|
||||||
|
Backbone.trigger('launcher-open-file', path);
|
||||||
|
};
|
||||||
|
if (window.launcherOpenedFile) {
|
||||||
|
Backbone.trigger('launcher-open-file', window.launcherOpenedFile);
|
||||||
|
delete window.launcherOpenedFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Launcher;
|
module.exports = Launcher;
|
||||||
|
|
|
@ -51,6 +51,7 @@ var AppView = Backbone.View.extend({
|
||||||
this.listenTo(Backbone, 'switch-view', this.switchView);
|
this.listenTo(Backbone, 'switch-view', this.switchView);
|
||||||
this.listenTo(Backbone, 'toggle-settings', this.toggleSettings);
|
this.listenTo(Backbone, 'toggle-settings', this.toggleSettings);
|
||||||
this.listenTo(Backbone, 'toggle-menu', this.toggleMenu);
|
this.listenTo(Backbone, 'toggle-menu', this.toggleMenu);
|
||||||
|
this.listenTo(Backbone, 'launcher-open-file', this.launcherOpenFile);
|
||||||
|
|
||||||
window.onbeforeunload = this.beforeUnload.bind(this);
|
window.onbeforeunload = this.beforeUnload.bind(this);
|
||||||
window.onresize = this.windowResize.bind(this);
|
window.onresize = this.windowResize.bind(this);
|
||||||
|
@ -71,7 +72,7 @@ var AppView = Backbone.View.extend({
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
showOpenFile: function() {
|
showOpenFile: function(filePath) {
|
||||||
this.views.menu.hide();
|
this.views.menu.hide();
|
||||||
this.views.menuDrag.hide();
|
this.views.menuDrag.hide();
|
||||||
this.views.list.hide();
|
this.views.list.hide();
|
||||||
|
@ -83,6 +84,15 @@ var AppView = Backbone.View.extend({
|
||||||
this.views.open = new OpenView({ model: this.model });
|
this.views.open = new OpenView({ model: this.model });
|
||||||
this.views.open.setElement(this.$el.find('.app__body')).render();
|
this.views.open.setElement(this.$el.find('.app__body')).render();
|
||||||
this.views.open.on('cancel', this.showEntries, this);
|
this.views.open.on('cancel', this.showEntries, this);
|
||||||
|
if (Launcher && filePath) {
|
||||||
|
this.views.open.showOpenLocalFile(filePath);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
launcherOpenFile: function(path) {
|
||||||
|
if (path && /\.kdbx$/i.test(path)) {
|
||||||
|
this.showOpenFile(path);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
showEntries: function() {
|
showEntries: function() {
|
||||||
|
@ -189,7 +199,21 @@ var AppView = Backbone.View.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
saveAll: function() {
|
saveAll: function() {
|
||||||
this.showFileSettings({ fileId: this.model.files.first().cid });
|
var fileId;
|
||||||
|
this.model.files.forEach(function(file) {
|
||||||
|
if (file.get('path')) {
|
||||||
|
try {
|
||||||
|
file.autoSave();
|
||||||
|
} catch (e) {
|
||||||
|
fileId = file.cid;
|
||||||
|
}
|
||||||
|
} else if (!fileId) {
|
||||||
|
fileId = file.cid;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (fileId) {
|
||||||
|
this.showFileSettings({fileId: fileId});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSettings: function(page) {
|
toggleSettings: function(page) {
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
var Backbone = require('backbone'),
|
var Backbone = require('backbone'),
|
||||||
PasswordGenerator = require('../util/password-generator'),
|
PasswordGenerator = require('../util/password-generator'),
|
||||||
AppSettingsModel = require('../models/app-settings-model'),
|
|
||||||
CopyPaste = require('../util/copy-paste');
|
CopyPaste = require('../util/copy-paste');
|
||||||
|
|
||||||
|
var DefaultGenOpts = {
|
||||||
|
length: 16, upper: true, lower: true, digits: true, special: false, brackets: false, high: false, ambiguous: false
|
||||||
|
};
|
||||||
|
|
||||||
var GeneratorView = Backbone.View.extend({
|
var GeneratorView = Backbone.View.extend({
|
||||||
el: 'body',
|
el: 'body',
|
||||||
|
|
||||||
|
@ -21,7 +24,7 @@ var GeneratorView = Backbone.View.extend({
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
$('body').one('click', this.remove.bind(this));
|
$('body').one('click', this.remove.bind(this));
|
||||||
this.gen = _.clone(AppSettingsModel.instance.get('genOpts'));
|
this.gen = _.clone(DefaultGenOpts);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
var Backbone = require('backbone'),
|
var Backbone = require('backbone'),
|
||||||
OpenFileView = require('./open-file-view'),
|
OpenFileView = require('./open-file-view'),
|
||||||
FileModel = require('../models/file-model');
|
FileModel = require('../models/file-model'),
|
||||||
|
Launcher = require('../util/launcher');
|
||||||
|
|
||||||
var OpenView = Backbone.View.extend({
|
var OpenView = Backbone.View.extend({
|
||||||
template: require('templates/open.html'),
|
template: require('templates/open.html'),
|
||||||
|
@ -94,6 +95,22 @@ var OpenView = Backbone.View.extend({
|
||||||
if (dataFile) {
|
if (dataFile) {
|
||||||
this.views.openFile.setFile(dataFile, keyFile);
|
this.views.openFile.setFile(dataFile, keyFile);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showOpenLocalFile: function(path) {
|
||||||
|
if (path && Launcher) {
|
||||||
|
try {
|
||||||
|
var name = path.match(/[^/\\]*$/)[0];
|
||||||
|
var data = Launcher.readFile(path);
|
||||||
|
var file = new Blob([data]);
|
||||||
|
Object.defineProperties(file, {
|
||||||
|
path: { value: path },
|
||||||
|
name: { value: name }
|
||||||
|
});
|
||||||
|
this.views.openFile.setFile(file);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Backbone = require('backbone'),
|
var Backbone = require('backbone'),
|
||||||
|
AppSettingsModel = require('../../models/app-settings-model'),
|
||||||
FeatureDetector = require('../../util/feature-detector'),
|
FeatureDetector = require('../../util/feature-detector'),
|
||||||
PasswordDisplay = require('../../util/password-display'),
|
PasswordDisplay = require('../../util/password-display'),
|
||||||
Alerts = require('../../util/alerts'),
|
Alerts = require('../../util/alerts'),
|
||||||
RuntimeInfo = require('../../util/runtime-info'),
|
RuntimeInfo = require('../../util/runtime-info'),
|
||||||
|
Launcher = require('../../util/launcher'),
|
||||||
Links = require('../../const/links'),
|
Links = require('../../const/links'),
|
||||||
kdbxweb = require('kdbxweb'),
|
kdbxweb = require('kdbxweb'),
|
||||||
FileSaver = require('filesaver');
|
FileSaver = require('filesaver');
|
||||||
|
@ -96,8 +98,37 @@ var SettingsAboutView = Backbone.View.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var data = this.model.getData();
|
var data = this.model.getData();
|
||||||
var blob = new Blob([data], {type: 'application/octet-stream'});
|
var fileName = this.model.get('name') + '.kdbx';
|
||||||
FileSaver.saveAs(blob, this.model.get('name') + '.kdbx');
|
if (Launcher) {
|
||||||
|
if (this.model.get('path')) {
|
||||||
|
this.saveToFileWithPath(this.model.get('path'), data);
|
||||||
|
} else {
|
||||||
|
Launcher.getSaveFileName(fileName, (function (path) {
|
||||||
|
if (path) {
|
||||||
|
this.saveToFileWithPath(path, data);
|
||||||
|
}
|
||||||
|
}).bind(this));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var blob = new Blob([data], {type: 'application/octet-stream'});
|
||||||
|
FileSaver.saveAs(blob, fileName);
|
||||||
|
this.model.saved();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveToFileWithPath: function(path, data) {
|
||||||
|
try {
|
||||||
|
Launcher.writeFile(path, data);
|
||||||
|
this.model.saved(path);
|
||||||
|
if (!AppSettingsModel.instance.get('lastOpenFile')) {
|
||||||
|
AppSettingsModel.instance.set('lastOpenFile', path);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Alerts.error({
|
||||||
|
header: 'Save error',
|
||||||
|
body: 'Error saving to file ' + path + ': \n' + e
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
exportAsXml: function() {
|
exportAsXml: function() {
|
||||||
|
|
|
@ -30,7 +30,6 @@ var SettingsGeneralView = Backbone.View.extend({
|
||||||
changeTheme: function(e) {
|
changeTheme: function(e) {
|
||||||
var theme = e.target.value;
|
var theme = e.target.value;
|
||||||
AppSettingsModel.instance.set('theme', theme);
|
AppSettingsModel.instance.set('theme', theme);
|
||||||
AppSettingsModel.instance.save();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
openDevTools: function() {
|
openDevTools: function() {
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
<div class="open__file-password">
|
<div class="open__file-password">
|
||||||
<div class="open__file-title">
|
<div class="open__file-title">
|
||||||
<% if (name) { %>
|
<% if (name) { %>
|
||||||
<a class="open__file-link-name <%= opening ? 'disabled' : '' %> muted-color"><%- name %></a>
|
<a class="open__file-link-name <%= opening ? 'disabled' : '' %> muted-color"><%- name %></a> / <a
|
||||||
|
class="open__file-link-name muted-color" <%= opening ? 'disabled' : '' %>>Another</a>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<a class="open__file-link-open muted-color" <%= opening ? 'disabled' : '' %>>Open</a> / <a
|
<a class="open__file-link-open muted-color" <%= opening ? 'disabled' : '' %>>Open</a> / <a
|
||||||
class="open__file-link-new muted-color" <%= opening ? 'disabled' : '' %>>New</a> / <a
|
class="open__file-link-new muted-color" <%= opening ? 'disabled' : '' %>>New</a> / <a
|
||||||
|
@ -27,4 +28,4 @@
|
||||||
<i class="open__file-btn-key fa fa-key <%= (keyFileName && !opening) ? 'open__file-btn-key--active' : '' %>"
|
<i class="open__file-btn-key fa fa-key <%= (keyFileName && !opening) ? 'open__file-btn-key--active' : '' %>"
|
||||||
<%= (name && !opening) ? '' : 'disabled' %>></i>
|
<%= (name && !opening) ? '' : 'disabled' %>></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,14 +5,16 @@ var app = require('app'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
|
|
||||||
var mainWindow = null;
|
var mainWindow = null,
|
||||||
|
openFile = null,
|
||||||
|
ready = false;
|
||||||
|
|
||||||
app.on('window-all-closed', function() { app.quit(); });
|
app.on('window-all-closed', function() { app.quit(); });
|
||||||
|
|
||||||
app.on('ready', function() {
|
app.on('ready', function() {
|
||||||
var htmlPath = path.join(app.getPath('userData'), 'index.html');
|
var htmlPath = path.join(app.getPath('userData'), 'index.html');
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
width: 1000, height: 700, 'min-width': 600, 'min-height': 300,
|
width: 1000, height: 700, 'min-width': 600, 'min-height': 300,
|
||||||
icon: path.join(__dirname, 'icon.png')
|
icon: path.join(__dirname, 'icon.png')
|
||||||
});
|
});
|
||||||
|
@ -22,5 +24,22 @@ app.on('ready', function() {
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadUrl('https://antelle.github.io/keeweb/index.html');
|
mainWindow.loadUrl('https://antelle.github.io/keeweb/index.html');
|
||||||
}
|
}
|
||||||
|
mainWindow.webContents.on('dom-ready', function() {
|
||||||
|
mainWindow.show();
|
||||||
|
ready = true;
|
||||||
|
notifyOpenFile();
|
||||||
|
});
|
||||||
mainWindow.on('closed', function() { mainWindow = null; });
|
mainWindow.on('closed', function() { mainWindow = null; });
|
||||||
});
|
});
|
||||||
|
app.on('open-file', function(e, path) {
|
||||||
|
e.preventDefault();
|
||||||
|
openFile = path.replace(/"/g, '\\"');
|
||||||
|
notifyOpenFile();
|
||||||
|
});
|
||||||
|
|
||||||
|
function notifyOpenFile() {
|
||||||
|
if (ready && openFile && mainWindow) {
|
||||||
|
mainWindow.webContents.executeJavaScript('if (window.launcherOpen) { window.launcherOpen("' + openFile + '"); } ' +
|
||||||
|
' else { window.launcherOpenedFile="' + openFile + '"; }');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue