file save/open

This commit is contained in:
Antelle 2015-10-24 22:06:44 +03:00
parent 058b82f722
commit 874477c5c4
12 changed files with 162 additions and 21 deletions

View File

@ -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'));
} }
}); });

View File

@ -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();
}, },

View File

@ -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 */ }
} }
}, },

View File

@ -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();

View File

@ -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;

View File

@ -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) {

View File

@ -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() {

View File

@ -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) {
}
}
} }
}); });

View File

@ -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() {

View File

@ -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() {

View File

@ -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>

View File

@ -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 + '"; }');
}
}