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() {
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',
file: file
});
if (file.get('path')) {
AppSettingsModel.instance.set('lastOpenFile', file.get('path'));
}
this.refresh();
},

View File

@ -5,19 +5,18 @@ var Backbone = require('backbone');
var AppSettingsModel = Backbone.Model.extend({
defaults: {
theme: 'd',
genOpts: {
length: 16, upper: true, lower: true, digits: true, special: false, brackets: false, high: false, ambiguous: false
}
lastOpenFile: ''
},
initialize: function() {
this.listenTo(this, 'change', this.save);
},
load: function() {
if (typeof localStorage !== 'undefined' && localStorage.appSettings) {
try {
var data = JSON.parse(localStorage.appSettings);
this.set(data);
this.set(data, { silent: true });
} catch (e) { /* failed to load settings */ }
}
},

View File

@ -3,6 +3,7 @@
var Backbone = require('backbone'),
GroupCollection = require('../collections/group-collection'),
GroupModel = require('./group-model'),
Launcher = require('../util/launcher'),
kdbxweb = require('kdbxweb'),
demoFileData = require('base64!../../resources/Demo.kdbx');
@ -54,9 +55,6 @@ var FileModel = Backbone.Model.extend({
}
this.readModel(this.get('name'));
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) {
@ -82,9 +80,14 @@ var FileModel = Backbone.Model.extend({
opening: false,
error: false,
oldKeyFileName: this.get('keyFileName'),
oldPasswordLength: props.passwordLength
oldPasswordLength: props.passwordLength,
passwordChanged: false,
keyFileChanged: false
});
this.set(props);
this._oldPasswordHash = this.db.credentials.passwordHash;
this._oldKeyFileHash = this.db.credentials.keyFileHash;
this._oldKeyChangeDate = this.db.meta.keyChanged;
},
readModel: function(topGroupTitle) {
@ -154,6 +157,10 @@ var FileModel = Backbone.Model.extend({
}
},
autoSave: function() {
Launcher.writeFile(this.get('path'), this.getData());
},
getData: function() {
return this.db.save();
},
@ -162,6 +169,14 @@ var FileModel = Backbone.Model.extend({
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) {
this.db.credentials.setPassword(password);
this.db.meta.keyChanged = new Date();

View File

@ -1,5 +1,6 @@
'use strict';
var Backbone = require('backbone');
var Launcher;
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,
openDevTools: function() {
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;

View File

@ -51,6 +51,7 @@ var AppView = Backbone.View.extend({
this.listenTo(Backbone, 'switch-view', this.switchView);
this.listenTo(Backbone, 'toggle-settings', this.toggleSettings);
this.listenTo(Backbone, 'toggle-menu', this.toggleMenu);
this.listenTo(Backbone, 'launcher-open-file', this.launcherOpenFile);
window.onbeforeunload = this.beforeUnload.bind(this);
window.onresize = this.windowResize.bind(this);
@ -71,7 +72,7 @@ var AppView = Backbone.View.extend({
return this;
},
showOpenFile: function() {
showOpenFile: function(filePath) {
this.views.menu.hide();
this.views.menuDrag.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.setElement(this.$el.find('.app__body')).render();
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() {
@ -189,7 +199,21 @@ var AppView = Backbone.View.extend({
},
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) {

View File

@ -2,9 +2,12 @@
var Backbone = require('backbone'),
PasswordGenerator = require('../util/password-generator'),
AppSettingsModel = require('../models/app-settings-model'),
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({
el: 'body',
@ -21,7 +24,7 @@ var GeneratorView = Backbone.View.extend({
initialize: function () {
$('body').one('click', this.remove.bind(this));
this.gen = _.clone(AppSettingsModel.instance.get('genOpts'));
this.gen = _.clone(DefaultGenOpts);
},
render: function() {

View File

@ -2,7 +2,8 @@
var Backbone = require('backbone'),
OpenFileView = require('./open-file-view'),
FileModel = require('../models/file-model');
FileModel = require('../models/file-model'),
Launcher = require('../util/launcher');
var OpenView = Backbone.View.extend({
template: require('templates/open.html'),
@ -94,6 +95,22 @@ var OpenView = Backbone.View.extend({
if (dataFile) {
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';
var Backbone = require('backbone'),
AppSettingsModel = require('../../models/app-settings-model'),
FeatureDetector = require('../../util/feature-detector'),
PasswordDisplay = require('../../util/password-display'),
Alerts = require('../../util/alerts'),
RuntimeInfo = require('../../util/runtime-info'),
Launcher = require('../../util/launcher'),
Links = require('../../const/links'),
kdbxweb = require('kdbxweb'),
FileSaver = require('filesaver');
@ -96,8 +98,37 @@ var SettingsAboutView = Backbone.View.extend({
return;
}
var data = this.model.getData();
var blob = new Blob([data], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, this.model.get('name') + '.kdbx');
var fileName = 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() {

View File

@ -30,7 +30,6 @@ var SettingsGeneralView = Backbone.View.extend({
changeTheme: function(e) {
var theme = e.target.value;
AppSettingsModel.instance.set('theme', theme);
AppSettingsModel.instance.save();
},
openDevTools: function() {

View File

@ -10,7 +10,8 @@
<div class="open__file-password">
<div class="open__file-title">
<% 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 { %>
<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
@ -27,4 +28,4 @@
<i class="open__file-btn-key fa fa-key <%= (keyFileName && !opening) ? 'open__file-btn-key--active' : '' %>"
<%= (name && !opening) ? '' : 'disabled' %>></i>
</div>
</div>
</div>

View File

@ -5,14 +5,16 @@ var app = require('app'),
path = require('path'),
fs = require('fs');
var mainWindow = null;
var mainWindow = null,
openFile = null,
ready = false;
app.on('window-all-closed', function() { app.quit(); });
app.on('ready', function() {
var htmlPath = path.join(app.getPath('userData'), 'index.html');
mainWindow = new BrowserWindow({
show: false,
width: 1000, height: 700, 'min-width': 600, 'min-height': 300,
icon: path.join(__dirname, 'icon.png')
});
@ -22,5 +24,22 @@ app.on('ready', function() {
} else {
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; });
});
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 + '"; }');
}
}